diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index ef275abe466..facec27d6a4 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -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 diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/rewrite/ASTRewrite.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/rewrite/ASTRewrite.java new file mode 100644 index 00000000000..047250ec6b5 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/rewrite/ASTRewrite.java @@ -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. + *
+ * The initial implementation does not support nodes that implement + * {@link IASTPreprocessorStatement}, {@link IASTComment} or {@link IASTProblem}. + *
+ * EXPERIMENTAL. 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. + *
+ * @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, ornull
+ * @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 null
+ * @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 null
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 null
+ * @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.
+ * + * 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. + *
+ * + * @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$ + } + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java new file mode 100644 index 00000000000..01227434487 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTLiteralNode.java @@ -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) { + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModification.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModification.java new file mode 100644 index 00000000000..aa886c3a2aa --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModification.java @@ -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, ornull
+ */
+ public IASTNode getNewNode() {
+ return fNewNode;
+ }
+
+ /**
+ * Returns the edit group to collect the text edits of this modification.
+ * @return the edit group or null
.
+ */
+ public TextEditGroup getAssociatedEditGroup() {
+ return fTextEditGroup;
+ }
+}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java
new file mode 100644
index 00000000000..eaf332e224b
--- /dev/null
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java
@@ -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 HashMapnull
.
+ */
+ public Listnull
+ * @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 null
if there
+ * are no modifications.
+ * @return the root modifications or null
.
+ */
+ 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, null
is returned.
+ * @return the nested modifications or null
.
+ */
+ public ASTModificationMap getNestedModifications(ASTModification mod) {
+ return fNestedModMaps.get(mod);
+ }
+}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java
new file mode 100644
index 00000000000..311a1c25cee
--- /dev/null
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java
@@ -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$
+ }
+
+}