1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Proposal for AST-rewriter API, bug 214334.

This commit is contained in:
Markus Schorn 2008-01-09 14:37:20 +00:00
parent 000b5c50e2
commit 88ac1b7a85
7 changed files with 518 additions and 1 deletions

View file

@ -19,6 +19,7 @@ Export-Package: org.eclipse.cdt.core,
org.eclipse.cdt.core.dom.parser,
org.eclipse.cdt.core.dom.parser.c,
org.eclipse.cdt.core.dom.parser.cpp,
org.eclipse.cdt.core.dom.rewrite,
org.eclipse.cdt.core.envvar,
org.eclipse.cdt.core.formatter,
org.eclipse.cdt.core.index,
@ -45,6 +46,7 @@ Export-Package: org.eclipse.cdt.core,
org.eclipse.cdt.internal.core.dom.parser;x-friends:="org.eclipse.cdt.refactoring",
org.eclipse.cdt.internal.core.dom.parser.c;x-friends:="org.eclipse.cdt.refactoring",
org.eclipse.cdt.internal.core.dom.parser.cpp;x-friends:="org.eclipse.cdt.ui,org.eclipse.cdt.refactoring",
org.eclipse.cdt.internal.core.dom.rewrite;x-friends:="org.eclipse.cdt.core.tests",
org.eclipse.cdt.internal.core.envvar,
org.eclipse.cdt.internal.core.index;x-friends:="org.eclipse.cdt.ui",
org.eclipse.cdt.internal.core.index.provider,
@ -92,6 +94,7 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="[3.2.0,4.0.0)",
org.eclipse.text;bundle-version="[3.2.0,4.0.0)",
org.eclipse.core.variables;bundle-version="[3.1.100,4.0.0)",
org.eclipse.core.filebuffers;bundle-version="[3.2.0,4.0.0)",
org.eclipse.core.filesystem;bundle-version="[1.1.0,2.0.0)"
org.eclipse.core.filesystem;bundle-version="[1.1.0,2.0.0)",
org.eclipse.ltk.core.refactoring;bundle-version="3.4.0"
Eclipse-LazyStart: true
Bundle-RequiredExecutionEnvironment: J2SE-1.5

View file

@ -0,0 +1,201 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.dom.rewrite;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTLiteralNode;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModificationStore;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTRewriteAnalyzer;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.text.edits.TextEditGroup;
/**
* Infrastructure for modifying code by describing changes to AST nodes.
* The AST rewriter collects descriptions of modifications to nodes and
* translates these descriptions into text edits that can then be applied to
* the original source. This is all done without actually modifying the
* original AST. The rewrite infrastructure tries to generate minimal
* text changes, preserve existing comments and indentation, and follow code
* formatter settings.
* <p>
* The initial implementation does not support nodes that implement
* {@link IASTPreprocessorStatement}, {@link IASTComment} or {@link IASTProblem}.
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is no guarantee that this API will
* work or that it will remain the same. Please do not use this API without
* consulting with the CDT team.
* </p>
* @since 5.0
*/
public final class ASTRewrite {
/**
* Creates a rewriter for a translation unit.
*/
public static ASTRewrite create(IASTTranslationUnit node) {
return new ASTRewrite(node, new ASTModificationStore(), null);
}
private final IASTNode fRoot;
private final ASTModificationStore fModificationStore;
private final ASTModification fParentMod;
public ASTRewrite(IASTNode root, ASTModificationStore modStore, ASTModification parentMod) {
fRoot= root;
fModificationStore= modStore;
fParentMod= parentMod;
}
/**
* Creates and returns a node for a source string that is to be inserted into
* the output document.
* The string will be inserted without being reformatted beyond correcting
* the indentation level.
*
* @param code the string to be inserted; lines should not have extra indentation
* @return a synthetic node representing the literal code.
* @throws IllegalArgumentException if the code is null.
*/
public final IASTNode createLiteralNode(String code) {
return new ASTLiteralNode(code);
}
/**
* Removes the given node in this rewriter. The ast is not modified, the rewriter
* just records the removal.
*
* @param node the node being removed
* @param editGroup the edit group in which to collect the corresponding
* text edits, or <code>null</code>
* @throws IllegalArgumentException if the node is null, the node is not
* part of this rewriter's AST.
*/
public final void remove(IASTNode node, TextEditGroup editGroup) {
checkBelongsToAST(node);
checkSupportedNode(node);
ASTModification mod= new ASTModification(ModificationKind.REPLACE, node, null, editGroup);
fModificationStore.storeModification(fParentMod, mod);
}
/**
* Replaces the given node in this rewriter. The ast is not modified, the rewriter
* just records the replacement.
* The replacement node can be part of a translation-unit or it is a synthetic
* (newly created) node.
*
* @param node the node being replaced
* @param replacement the node replacing the given one
* @param editGroup the edit group in which to collect the corresponding
* text edits, or <code>null</code>
* @return a rewriter for further rewriting the replacement node.
* @throws IllegalArgumentException if the node or the replacement is null, or if the node is not
* part of this rewriter's AST
*/
public final ASTRewrite replace(IASTNode node, IASTNode replacement, TextEditGroup editGroup) {
if (replacement == null) {
throw new IllegalArgumentException();
}
checkBelongsToAST(node);
checkSupportedNode(node);
checkSupportedNode(replacement);
ASTModification mod= new ASTModification(ModificationKind.REPLACE, node, replacement, editGroup);
fModificationStore.storeModification(fParentMod, mod);
return new ASTRewrite(replacement, fModificationStore, mod);
}
/**
* Inserts the given node in this rewriter. The ast is not modified, the rewriter
* just records the insertion.
* The new node can be part of a translation-unit or it is a synthetic
* (newly created) node.
* @param parent the parent the new node is added to.
* @param insertionPoint the node before which the insertion shall be done, or <code>null</code> for inserting after the last child.
* @param newNode the node being inserted
* @param editGroup the edit group in which to collect the corresponding
* text edits, or <code>null</code>
* @return a rewriter for further rewriting the inserted node.
* @throws IllegalArgumentException if the parent or the newNode is null, or if the parent is not
* part of this rewriter's AST, or the insertionPoint is not a child of the parent.
*/
public final ASTRewrite insertBefore(IASTNode parent, IASTNode insertionPoint, IASTNode newNode, TextEditGroup editGroup) {
if (parent != fRoot) {
checkBelongsToAST(parent);
}
if (newNode == null) {
throw new IllegalArgumentException();
}
checkSupportedNode(parent);
checkSupportedNode(insertionPoint);
checkSupportedNode(newNode);
ASTModification mod;
if (insertionPoint == null) {
mod= new ASTModification(ModificationKind.APPEND_CHILD, parent, newNode, editGroup);
}
else {
if (insertionPoint.getParent() != parent) {
throw new IllegalArgumentException();
}
mod= new ASTModification(ModificationKind.INSERT_BEFORE, insertionPoint, newNode, editGroup);
}
fModificationStore.storeModification(fParentMod, mod);
return new ASTRewrite(newNode, fModificationStore, mod);
}
/**
* Converts all modifications recorded by this rewriter into the change object required by the
* refactoring framework.
* <p>
* Calling this methods does not discard the modifications on record. Subsequence modifications
* are added to the ones already on record. If this method is called again later,
* the resulting text edit object will accurately reflect the net cumulative affect of all those changes.
* </p>
*
* @return Change object describing the changes to the
* document corresponding to the changes recorded by this rewriter
* @since 5.0
*/
public Change rewriteAST() {
if (!(fRoot instanceof IASTTranslationUnit)) {
throw new IllegalArgumentException("This API can only be used for the root rewrite object."); //$NON-NLS-1$
}
return ASTRewriteAnalyzer.rewriteAST((IASTTranslationUnit) fRoot, fModificationStore);
}
private void checkBelongsToAST(IASTNode node) {
while (node != null) {
node= node.getParent();
if (node == fRoot) {
return;
}
}
throw new IllegalArgumentException();
}
private void checkSupportedNode(IASTNode node) {
if (node instanceof IASTComment) {
throw new IllegalArgumentException("Rewriting comments is not yet supported"); //$NON-NLS-1$
}
if (node instanceof IASTPreprocessorStatement) {
throw new IllegalArgumentException("Rewriting preprocessor statements is not yet supported"); //$NON-NLS-1$
}
if (node instanceof IASTProblem) {
throw new IllegalArgumentException("Rewriting problem nodes is supported"); //$NON-NLS-1$
}
}
}

View file

@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
/**
* Used for inserting literal code by means of the rewrite facility. The node
* will never appear in an AST tree.
* @since 5.0
*/
public class ASTLiteralNode implements IASTNode {
private final String fCode;
public ASTLiteralNode(String code) {
fCode= code;
}
public String getRawSignature() {
return fCode;
}
public boolean accept(ASTVisitor visitor) {
return false;
}
public boolean contains(IASTNode node) {
return false;
}
public String getContainingFilename() {
return null;
}
public IASTFileLocation getFileLocation() {
return null;
}
public IASTNodeLocation[] getNodeLocations() {
return null;
}
public IASTNode getParent() {
return null;
}
public ASTNodeProperty getPropertyInParent() {
return null;
}
public IASTTranslationUnit getTranslationUnit() {
return null;
}
public boolean isPartOfTranslationUnitFile() {
return false;
}
public void setParent(IASTNode node) {
}
public void setPropertyInParent(ASTNodeProperty property) {
}
}

View file

@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.text.edits.TextEditGroup;
public class ASTModification {
public enum ModificationKind {
REPLACE,
INSERT_BEFORE,
APPEND_CHILD
}
private final ModificationKind fKind;
private final IASTNode fTargetNode;
private final IASTNode fNewNode;
private final TextEditGroup fTextEditGroup;
public ASTModification(ModificationKind kind, IASTNode targetNode, IASTNode newNode, TextEditGroup group) {
fKind= kind;
fTargetNode= targetNode;
fNewNode= newNode;
fTextEditGroup= group;
}
/**
* @return the kind of the modification
*/
public ModificationKind getKind() {
return fKind;
}
/**
* Return the target node of this modification.
*/
public IASTNode getTargetNode() {
return fTargetNode;
}
/**
* Return the new node of this modification, or <code>null</code>
*/
public IASTNode getNewNode() {
return fNewNode;
}
/**
* Returns the edit group to collect the text edits of this modification.
* @return the edit group or <code>null</code>.
*/
public TextEditGroup getAssociatedEditGroup() {
return fTextEditGroup;
}
}

View file

@ -0,0 +1,88 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind;
/**
* Represents a list of modifications to an ast-node. If there are nested modifications
* to nodes introduced by insertions or replacements, these modifications are collected
* in separate modification maps. I.e. a modification map represents one level of
* modifications.
* @see ASTModificationStore
* @since 5.0
*/
public class ASTModificationMap {
private HashMap<IASTNode,List<ASTModification>> fModifications= new HashMap<IASTNode,List<ASTModification>>();
/**
* Adds a modification to this modification map.
*/
public void addModification(ASTModification mod) {
final IASTNode targetNode = mod.getTargetNode();
List<ASTModification> mods= fModifications.get(targetNode);
if (mods == null || mods.isEmpty()) {
mods= new ArrayList<ASTModification>();
mods.add(mod);
fModifications.put(targetNode, mods);
}
else {
switch (mod.getKind()) {
case REPLACE:
if (mods.get(mods.size()-1).getKind() != ModificationKind.INSERT_BEFORE ) {
throw new IllegalArgumentException("Attempt to replace a node that has been modified"); //$NON-NLS-1$
}
mods.add(mod);
break;
case APPEND_CHILD:
if (mods.get(mods.size()-1).getKind() == ModificationKind.REPLACE) {
throw new IllegalArgumentException("Attempt to modify a node that has been replaced"); //$NON-NLS-1$
}
mods.add(mod);
break;
case INSERT_BEFORE:
int i;
for (i=mods.size()-1; i>=0; i--) {
if (mods.get(i).getKind() == ModificationKind.INSERT_BEFORE) {
break;
}
}
mods.add(i+1, mod);
break;
}
}
}
/**
* Returns the list of modifications for a given node. The list can contain different modifications.
* It is guaranteed that INSERT_BEFORE modifications appear first. Furthermore, if there is a
* REPLACE modification the list will not contain any other REPLACE or APPEND_CHILD modifications.
* @return the modification list or <code>null</code>.
*/
public List<ASTModification> getModificationsForNode(IASTNode node) {
return Collections.unmodifiableList(fModifications.get(node));
}
/**
* Returns the collection of nodes that are modified by this modification map.
*/
public Collection<IASTNode> getModifiedNodes() {
return Collections.unmodifiableCollection(fModifications.keySet());
}
}

View file

@ -0,0 +1,63 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite;
import java.util.HashMap;
/**
* Collects modifications to an AST in a hierarchical manner. The store gives access to
* the root modifications and for each modification you can obtain the nested modifications here.
* @since 5.0
*/
public class ASTModificationStore {
private HashMap<ASTModification,ASTModificationMap> fNestedModMaps;
public ASTModificationStore() {
fNestedModMaps= new HashMap<ASTModification, ASTModificationMap>();
}
/**
* Adds a potentially nested modification to the store.
* @param parentMod the parent for a nested modification, or <code>null</code>
* @param mod a modification
*/
public void storeModification(ASTModification parentMod, ASTModification mod) {
ASTModificationMap modMap = createNestedModificationMap(parentMod);
modMap.addModification(mod);
}
private ASTModificationMap createNestedModificationMap(ASTModification parentMod) {
ASTModificationMap modMap= fNestedModMaps.get(parentMod);
if (modMap == null) {
modMap= new ASTModificationMap();
fNestedModMaps.put(parentMod, modMap);
}
return modMap;
}
/**
* Returns the modifications that are performed directly on the AST, or <code>null</code> if there
* are no modifications.
* @return the root modifications or <code>null</code>.
*/
public ASTModificationMap getRootModifications() {
return fNestedModMaps.get(null);
}
/**
* Returns the modifications that are performed on the node that has been introduced by the
* given modification. If there are no nested modifications, <code>null</code> is returned.
* @return the nested modifications or <code>null</code>.
*/
public ASTModificationMap getNestedModifications(ASTModification mod) {
return fNestedModMaps.get(mod);
}
}

View file

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.ltk.core.refactoring.Change;
public class ASTRewriteAnalyzer {
public static Change rewriteAST(IASTTranslationUnit root, ASTModificationStore modificationStore) {
throw new UnsupportedOperationException("The rewriter is not yet implemented"); //$NON-NLS-1$
}
}