Index: dom/org/eclipse/jdt/core/dom/rewrite/ASTRewrite.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/rewrite/ASTRewrite.java,v retrieving revision 1.19 diff -u -r1.19 ASTRewrite.java --- dom/org/eclipse/jdt/core/dom/rewrite/ASTRewrite.java 21 Mar 2005 10:31:55 -0000 1.19 +++ dom/org/eclipse/jdt/core/dom/rewrite/ASTRewrite.java 19 Apr 2005 17:03:18 -0000 @@ -178,10 +178,14 @@ if (rootNode != null) { //validateASTNotModified(rootNode); - getRewriteEventStore().markMovedNodesRemoved(); + TargetSourceRangeComputer sourceRangeComputer= getExtendedSourceRangeComputer(); + + this.eventStore.prepareMovedNodes(sourceRangeComputer); - ASTRewriteAnalyzer visitor= new ASTRewriteAnalyzer(document, result, this.eventStore, this.nodeStore, options, getExtendedSourceRangeComputer()); + ASTRewriteAnalyzer visitor= new ASTRewriteAnalyzer(document, result, this.eventStore, this.nodeStore, options, sourceRangeComputer); rootNode.accept(visitor); // throws IllegalArgumentException + + this.eventStore.revertMovedNodes(); } return result; } Index: dom/org/eclipse/jdt/core/dom/rewrite/ListRewrite.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/rewrite/ListRewrite.java,v retrieving revision 1.9 diff -u -r1.9 ListRewrite.java --- dom/org/eclipse/jdt/core/dom/rewrite/ListRewrite.java 24 Mar 2005 17:25:13 -0000 1.9 +++ dom/org/eclipse/jdt/core/dom/rewrite/ListRewrite.java 19 Apr 2005 17:03:19 -0000 @@ -13,19 +13,18 @@ import java.util.Collections; import java.util.List; -import org.eclipse.text.edits.TextEditGroup; - import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; - import org.eclipse.jdt.internal.core.dom.rewrite.ListRewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.NodeInfoStore; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore.CopySourceInfo; +import org.eclipse.text.edits.TextEditGroup; /** * For describing manipulations to a child list property of an AST node. @@ -290,18 +289,19 @@ } - private ASTNode createTargetNode(ASTNode first, ASTNode last, boolean isMove) { + private ASTNode createTargetNode(ASTNode first, ASTNode last, boolean isMove, ASTNode replacingNode, TextEditGroup editGroup) { if (first == null || last == null) { throw new IllegalArgumentException(); } - //validateIsInsideAST(node); - CopySourceInfo info= getRewriteStore().markAsRangeCopySource(this.parent, this.childProperty, first, last, isMove); - + NodeInfoStore nodeStore= this.rewriter.getNodeStore(); ASTNode placeholder= nodeStore.newPlaceholderNode(first.getNodeType()); // revisit: could use list type if (placeholder == null) { throw new IllegalArgumentException("Creating a target node is not supported for nodes of type" + first.getClass().getName()); //$NON-NLS-1$ } + + Block internalPlaceHolder= nodeStore.createCollapsePlaceholder(); + CopySourceInfo info= getRewriteStore().createRangeCopy(this.parent, this.childProperty, first, last, isMove, internalPlaceHolder, replacingNode, editGroup); nodeStore.markAsCopyTarget(placeholder, info); return placeholder; @@ -318,14 +318,16 @@ * @param first the node that starts the range * @param last the node that ends the range * @return the new placeholder node - * @throws IllegalArgumentException if the node is null, or if the node - * is not part of this rewriter's AST + * @throws IllegalArgumentException An exception is thrown if the first or last node + * are null, if a node is not a child of the current list or if the first node + * is not before the last node. An IllegalArgumentException is + * also thrown if the copied range is overlapping with an other moved or copied range. */ public final ASTNode createCopyTarget(ASTNode first, ASTNode last) { if (first == last) { return this.rewriter.createCopyTarget(first); } else { - return createTargetNode(first, last, false); + return createTargetNode(first, last, false, null, null); } } @@ -340,16 +342,47 @@ * @param first the node that starts the range * @param last the node that ends the range * @return the new placeholder node - * @throws IllegalArgumentException if the node is null, or if the node - * is not part of this rewriter's AST + * @throws IllegalArgumentException An exception is thrown if the first or last node + * are null, if a node is not a child of the current list or if the first node + * is not before the last node. An IllegalArgumentException is + * also thrown if the moved range is overlapping with an other moved or copied range. * * @since 3.1 */ public final ASTNode createMoveTarget(ASTNode first, ASTNode last) { + return createMoveTarget(first, last, null, null); + } + + /** + * Creates and returns a placeholder node for a move of a range of nodes of the + * current list. The moved nodes can optionally be replaced by a specified node. + * + * The placeholder node can either be inserted as new or used to replace an + * existing node. When the document is rewritten, a copy of the source code + * for the given node range is inserted into the output document at the position + * corresponding to the placeholder (indentation is adjusted). + * + * @param first the node that starts the range + * @param last the node that ends the range + * @param replacingNode a node that is set at the location of the moved nodes + * or null to remove all nodes + * @param editGroup the edit group in which to collect the corresponding + * text edits fro a replace, or null if ungrouped + * @return the new placeholder node + * @throws IllegalArgumentException An exception is thrown if the first or + * last node are null, if a node is not a child of the current list or + * if the first node is not before the last node. An IllegalArgumentException + * is also thrown if the moved range is overlapping with an other moved + * or copied range. + * + * @since 3.1 + */ + public final ASTNode createMoveTarget(ASTNode first, ASTNode last, ASTNode replacingNode, TextEditGroup editGroup) { if (first == last) { + replace(first, replacingNode, editGroup); return this.rewriter.createMoveTarget(first); } else { - return createTargetNode(first, last, true); + return createTargetNode(first, last, true, replacingNode, editGroup); } } Index: dom/org/eclipse/jdt/core/dom/rewrite/TargetSourceRangeComputer.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/rewrite/TargetSourceRangeComputer.java,v retrieving revision 1.3 diff -u -r1.3 TargetSourceRangeComputer.java --- dom/org/eclipse/jdt/core/dom/rewrite/TargetSourceRangeComputer.java 21 Mar 2005 10:31:55 -0000 1.3 +++ dom/org/eclipse/jdt/core/dom/rewrite/TargetSourceRangeComputer.java 19 Apr 2005 17:03:19 -0000 @@ -125,9 +125,11 @@ * that should be replaced (or deleted) */ public SourceRange computeSourceRange(ASTNode node) { - CompilationUnit cu = (CompilationUnit) node.getRoot(); - return new SourceRange( - cu.getExtendedStartPosition(node), - cu.getExtendedLength(node)); + ASTNode root= node.getRoot(); + if (root instanceof CompilationUnit) { + CompilationUnit cu= (CompilationUnit) root; + return new SourceRange(cu.getExtendedStartPosition(node), cu.getExtendedLength(node)); + } + return new SourceRange(node.getStartPosition(), node.getLength()); } } Index: dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java,v retrieving revision 1.29 diff -u -r1.29 ASTRewriteAnalyzer.java --- dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java 7 Apr 2005 08:41:39 -0000 1.29 +++ dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java 19 Apr 2005 17:03:21 -0000 @@ -114,41 +114,33 @@ } /** - * Returns the extended source range computer for this AST rewriter. + * Returns the extended source range for a node. * - * @return an extended source range computer (never null) + * @return an extended source range (never null) * @since 3.1 */ - private TargetSourceRangeComputer getExtendedSourceRangeComputer() { - return this.extendedSourceRangeComputer; - } - final SourceRange getExtendedRange(ASTNode node) { - return getExtendedSourceRangeComputer().computeSourceRange(node); + if (node.getParent() == null) { + return new SourceRange(node.getStartPosition(), node.getLength()); + } + return this.extendedSourceRangeComputer.computeSourceRange(node); } final int getExtendedOffset(ASTNode node) { - return getExtendedSourceRangeComputer().computeSourceRange(node).getStartPosition(); + return getExtendedRange(node).getStartPosition(); } final int getExtendedEnd(ASTNode node) { - TargetSourceRangeComputer.SourceRange range = - getExtendedSourceRangeComputer().computeSourceRange(node); + TargetSourceRangeComputer.SourceRange range= getExtendedRange(node); return range.getStartPosition() + range.getLength(); } final TextEdit getCopySourceEdit(CopySourceInfo info) { TextEdit edit= (TextEdit) this.sourceCopyInfoToEdit.get(info); if (edit == null) { - int start, end; - if (info.getStartNode() == info.getEndNode()) { - SourceRange range= getExtendedRange(info.getStartNode()); - start= range.getStartPosition(); - end= start + range.getLength(); - } else { - start= getExtendedOffset(info.getStartNode()); - end= getExtendedEnd(info.getEndNode()); - } + SourceRange range= getExtendedRange(info.getNode()); + int start= range.getStartPosition(); + int end= start + range.getLength(); if (info.isMove) { MoveSourceEdit moveSourceEdit= new MoveSourceEdit(start, end - start); moveSourceEdit.setTargetEdit(new MoveTargetEdit(0)); @@ -284,32 +276,18 @@ if (property.isChildProperty() && node != null) { return doVisit((ASTNode) node); } else if (property.isChildListProperty()) { - boolean hasRangeCopySources= this.eventStore.hasRangeCopySources(parent, property); - return doVisitList((List) node, offset, hasRangeCopySources); + return doVisitList((List) node, offset); } return offset; } - private int doVisitList(List list, int offset, boolean hasRangeCopySources) { - if (hasRangeCopySources) { - // list with copy source ranges - Stack nodeRangeEndStack= new Stack(); - int endPos= offset; - for (Iterator iter= list.iterator(); iter.hasNext();) { - ASTNode curr= ((ASTNode) iter.next()); - doCopySourcePreVisit(this.eventStore.getRangeCopySources(curr), nodeRangeEndStack); - endPos= doVisit(curr); - doCopySourcePostVisit(curr, nodeRangeEndStack); - } - return endPos; - } else { - int endPos= offset; - for (Iterator iter= list.iterator(); iter.hasNext();) { - ASTNode curr= ((ASTNode) iter.next()); - endPos= doVisit(curr); - } - return endPos; + private int doVisitList(List list, int offset) { + int endPos= offset; + for (Iterator iter= list.iterator(); iter.hasNext();) { + ASTNode curr= ((ASTNode) iter.next()); + endPos= doVisit(curr); } + return endPos; } final void voidVisit(ASTNode node) { @@ -321,42 +299,20 @@ if (property.isChildProperty() && node != null) { voidVisit((ASTNode) node); } else if (property.isChildListProperty()) { - boolean hasRangeCopySources= this.eventStore.hasRangeCopySources(parent, property); - voidVisitList((List) node, hasRangeCopySources); + voidVisitList((List) node); } } - private void voidVisitList(List list, boolean hasRangeCopySources) { - if (hasRangeCopySources) { - // list with copy source ranges - Stack nodeRangeEndStack= new Stack(); - for (Iterator iter= list.iterator(); iter.hasNext();) { - ASTNode curr= ((ASTNode) iter.next()); - doCopySourcePreVisit(this.eventStore.getRangeCopySources(curr), nodeRangeEndStack); - doVisit(curr); - doCopySourcePostVisit(curr, nodeRangeEndStack); - } - } else { - for (Iterator iter= list.iterator(); iter.hasNext();) { - doVisit(((ASTNode) iter.next())); - } + private void voidVisitList(List list) { + for (Iterator iter= list.iterator(); iter.hasNext();) { + doVisit(((ASTNode) iter.next())); } } private final boolean doVisitUnchangedChildren(ASTNode parent) { List properties= parent.structuralPropertiesForType(); for (int i= 0; i < properties.size(); i++) { - StructuralPropertyDescriptor property= (StructuralPropertyDescriptor) properties.get(i); - if (property.isChildProperty()) { - ASTNode child= (ASTNode) parent.getStructuralProperty(property); - if (child != null) { - voidVisit(child); - } - } else if (property.isChildListProperty()) { - List list= (List) parent.getStructuralProperty(property); - boolean hasRangeCopySources= this.eventStore.hasRangeCopySources(parent, property); - voidVisitList(list, hasRangeCopySources); - } + voidVisit(parent, (StructuralPropertyDescriptor) properties.get(i)); } return false; } @@ -409,8 +365,6 @@ protected RewriteEvent[] list; - private Stack copyRangeEndStack; - protected final ASTNode getOriginalNode(int index) { return (ASTNode) this.list[index].getOriginalValue(); } @@ -465,8 +419,6 @@ this.startPos= offset; this.list= getEvent(parent, property).getChildren(); - initCopyRangeChecks(parent, property); - int total= this.list.length; if (total == 0) { return this.startPos; @@ -543,17 +495,13 @@ if (i > lastNonDelete && separatorState == EXISTING) { // is last, remove previous separator: split delete to allow range copies doTextRemove(prevEnd, currPos - prevEnd, editGroup); // remove separator - checkForRangeStart(node); doTextRemoveAndVisit(currPos, currEnd - currPos, node, editGroup); // remove node - checkForRangeEnd(node); currPos= currEnd; prevEnd= currEnd; } else { // remove element and next separator int end= getStartOfNextNode(nextIndex, currEnd); // start of next - checkForRangeStart(node); doTextRemoveAndVisit(currPos, currEnd - currPos, node, getEditGroup(currEvent)); // remove node - checkForRangeEnd(node); doTextRemove(currEnd, end - currEnd, editGroup); // remove separator currPos= end; prevEnd= currEnd; @@ -566,17 +514,13 @@ TextEditGroup editGroup= getEditGroup(currEvent); ASTNode changed= (ASTNode) currEvent.getNewValue(); - checkForRangeStart(node); doTextRemoveAndVisit(currPos, currEnd - currPos, node, editGroup); doTextInsert(currPos, changed, getNodeIndent(i), true, editGroup); - checkForRangeEnd(node); prevEnd= currEnd; } else { // is unchanged ASTNode node= (ASTNode) currEvent.getOriginalValue(); - checkForRangeStart(node); voidVisit(node); - checkForRangeEnd(node); } if (i == lastNonInsert) { // last node or next nodes are all inserts separatorState= NONE; @@ -600,23 +544,6 @@ return currPos; } - private void initCopyRangeChecks(ASTNode parent, StructuralPropertyDescriptor property) { - if (ASTRewriteAnalyzer.this.eventStore.hasRangeCopySources(parent, property)) { - this.copyRangeEndStack= new Stack(); - } - } - - private void checkForRangeStart(ASTNode node) { - if (this.copyRangeEndStack != null) { - doCopySourcePreVisit(ASTRewriteAnalyzer.this.eventStore.getRangeCopySources(node), this.copyRangeEndStack); - } - } - - private void checkForRangeEnd(ASTNode node) { - if (this.copyRangeEndStack != null) { - doCopySourcePostVisit(node, this.copyRangeEndStack); - } - } } private int rewriteRequiredNode(ASTNode parent, StructuralPropertyDescriptor property) { @@ -1109,7 +1036,7 @@ String destIndentString= this.formatter.getIndentString(getCurrentLine(formatted, offset)); if (data instanceof CopyPlaceholderData) { // replace with a copy/move target CopySourceInfo copySource= ((CopyPlaceholderData) data).copySource; - int srcIndentLevel= getIndent(copySource.getStartNode().getStartPosition()); + int srcIndentLevel= getIndent(copySource.getNode().getStartPosition()); TextEdit sourceEdit= getCopySourceEdit(copySource); doTextCopy(sourceEdit, insertOffset, srcIndentLevel, destIndentString, editGroup); currPos= offset + curr.length; // continue to insert after the replaced string @@ -1297,7 +1224,7 @@ TextEdit edit= getCopySourceEdit(curr); addEdit(edit); this.currentEdit= edit; - nodeEndStack.push(curr.getEndNode()); + nodeEndStack.push(curr.getNode()); } } } Index: dom/org/eclipse/jdt/internal/core/dom/rewrite/RewriteEventStore.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/RewriteEventStore.java,v retrieving revision 1.7 diff -u -r1.7 RewriteEventStore.java --- dom/org/eclipse/jdt/internal/core/dom/rewrite/RewriteEventStore.java 24 Mar 2005 17:25:13 -0000 1.7 +++ dom/org/eclipse/jdt/internal/core/dom/rewrite/RewriteEventStore.java 19 Apr 2005 17:03:23 -0000 @@ -10,23 +10,12 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.dom.rewrite; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.eclipse.text.edits.TextEditGroup; +import java.util.*; import org.eclipse.jdt.core.Signature; - -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; +import org.eclipse.jdt.core.dom.*; +import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer; +import org.eclipse.text.edits.TextEditGroup; /** @@ -37,8 +26,8 @@ public final class PropertyLocation { - private ASTNode parent; - private StructuralPropertyDescriptor property; + private final ASTNode parent; + private final StructuralPropertyDescriptor property; public PropertyLocation(ASTNode parent, StructuralPropertyDescriptor property) { this.parent= parent; @@ -52,6 +41,19 @@ public StructuralPropertyDescriptor getProperty() { return this.property; } + + public boolean equals(Object obj) { + if (obj != null && obj.getClass().equals(this.getClass())) { + PropertyLocation other= (PropertyLocation) obj; + return other.getParent().equals(getParent()) && other.getProperty().equals(getProperty()); + } + return false; + } + + public int hashCode() { + return getParent().hashCode() + getProperty().hashCode(); + } + } /** @@ -92,19 +94,60 @@ } } - public static class CopySourceInfo { - public final ASTNode parent; - public final StructuralPropertyDescriptor childProperty; + public static class CopySourceInfo implements Comparable { + public final PropertyLocation location; // can be null, only used to mark as removed on move + private final ASTNode node; + public final boolean isMove; + + public CopySourceInfo(PropertyLocation location, ASTNode node, boolean isMove) { + this.location= location; + this.node= node; + this.isMove= isMove; + } + + public ASTNode getNode() { + return this.node; + } + + public int compareTo(Object o2) { + CopySourceInfo r2= (CopySourceInfo) o2; + + int startDiff= this.getNode().getStartPosition() - r2.getNode().getStartPosition(); + if (startDiff != 0) { + return startDiff; // insert before if start node is first + } + + if (r2.isMove != this.isMove) { + return this.isMove ? -1 : 1; // first move then copy + } + return 0; + } + + public String toString() { + StringBuffer buf= new StringBuffer(); + if (this.isMove) { + buf.append("move source: "); //$NON-NLS-1$ + } else { + buf.append("copy source: "); //$NON-NLS-1$ + } + buf.append(this.node); + return buf.toString(); + } + } + + private static class NodeRangeInfo implements Comparable { private final ASTNode first; private final ASTNode last; - public final boolean isMove; + public final CopySourceInfo copyInfo; // containing the internal placeholder and the 'isMove' flag + public final ASTNode replacingNode; + public final TextEditGroup editGroup; - public CopySourceInfo(ASTNode parent, StructuralPropertyDescriptor childProperty, ASTNode first, ASTNode last, boolean isMove) { - this.parent= parent; - this.childProperty= childProperty; + public NodeRangeInfo(ASTNode parent, StructuralPropertyDescriptor childProperty, ASTNode first, ASTNode last, CopySourceInfo copyInfo, ASTNode replacingNode, TextEditGroup editGroup) { this.first= first; this.last= last; - this.isMove= isMove; + this.copyInfo= copyInfo; + this.replacingNode= replacingNode; + this.editGroup= editGroup; } public ASTNode getStartNode() { @@ -114,39 +157,59 @@ public ASTNode getEndNode() { return this.last; } - + + public boolean isMove() { + return this.copyInfo.isMove; + } + + public Block getInternalPlaceholder() { + return (Block) this.copyInfo.getNode(); + } + + public int compareTo(Object o2) { + NodeRangeInfo r2= (NodeRangeInfo) o2; + + int startDiff= this.getStartNode().getStartPosition() - r2.getStartNode().getStartPosition(); + if (startDiff != 0) { + return startDiff; // insert before if start node is first + } + int endDiff= this.getEndNode().getStartPosition() - r2.getEndNode().getStartPosition(); + if (endDiff != 0) { + return -endDiff; // insert before if length is longer + } + if (r2.isMove() != this.isMove()) { + return this.isMove() ? -1 : 1; // first move then copy + } + return 0; + } + + public void updatePlaceholderSourceRanges(TargetSourceRangeComputer sourceRangeComputer) { + TargetSourceRangeComputer.SourceRange startRange= sourceRangeComputer.computeSourceRange(getStartNode()); + TargetSourceRangeComputer.SourceRange endRange= sourceRangeComputer.computeSourceRange(getEndNode()); + int startPos= startRange.getStartPosition(); + int endPos= endRange.getStartPosition() + endRange.getLength(); + + Block internalPlaceholder= getInternalPlaceholder(); + internalPlaceholder.setSourceRange(startPos, endPos - startPos); + } + public String toString() { StringBuffer buf= new StringBuffer(); if (this.first != this.last) { buf.append("range "); //$NON-NLS-1$ } - if (this.isMove) { + if (isMove()) { buf.append("move source: "); //$NON-NLS-1$ } else { buf.append("copy source: "); //$NON-NLS-1$ } buf.append(this.first); - if (this.first != this.last) { - buf.append(" - "); //$NON-NLS-1$ - buf.append(this.last); - } + buf.append(" - "); //$NON-NLS-1$ + buf.append(this.last); return buf.toString(); } - } - public static class CopySourceInfoSorter implements Comparator { - public int compare(Object o1, Object o2) { - CopySourceInfo e1= (CopySourceInfo) o1; - CopySourceInfo e2= (CopySourceInfo) o2; - if (e1.isMove) { - return -1; - } else if (e2.isMove) { - return 1; - } - return e2.getEndNode().getStartPosition() - e1.getEndNode().getStartPosition(); - } - } /** @@ -166,8 +229,8 @@ } else { this.sourceNodeIter= Collections.EMPTY_LIST.iterator(); } - if (RewriteEventStore.this.rangeCopySources != null) { - this.rangeNodeIter= RewriteEventStore.this.rangeCopySources.iterator(); + if (RewriteEventStore.this.nodeRangeInfos != null) { + this.rangeNodeIter= RewriteEventStore.this.nodeRangeInfos.keySet().iterator(); } else { this.rangeNodeIter= Collections.EMPTY_LIST.iterator(); } @@ -193,10 +256,10 @@ return ((EventHolder) this.eventIter.next()).parent; } if (this.sourceNodeIter.hasNext()) { - return ((CopySourceInfo) this.sourceNodeIter.next()).getStartNode(); + return ((CopySourceInfo) this.sourceNodeIter.next()).getNode(); } if (this.rangeNodeIter.hasNext()) { - return ((CopySourceInfo) this.rangeNodeIter.next()).parent; + return ((PropertyLocation) this.rangeNodeIter.next()).getParent(); } return this.trackedNodeIter.next(); } @@ -223,11 +286,11 @@ /** Maps events to group descriptions */ private Map editGroups; - /** Stores which nodes are source of a copy or move (list of CopyRangeSourceInfo)*/ + /** Stores which nodes are source of a copy or move (list of CopySourceInfo)*/ List nodeCopySources; - /** Stores which node ranges that are source of a copy or move (list of CopyRangeSourceInfo)*/ - List rangeCopySources; + /** Stores node ranges that are used to copy or move (map of )*/ + Map nodeRangeInfos; /** Stores which nodes are tracked and the corresponding edit group*/ Map trackedNodes; @@ -250,6 +313,7 @@ this.nodePropertyMapper= null; this.nodeCopySources= null; + this.nodeRangeInfos= null; } /** @@ -485,8 +549,8 @@ setTrackedNodeData(node, editGroup); } - public final CopySourceInfo markAsCopySource(ASTNode parent, StructuralPropertyDescriptor property, ASTNode node, boolean isMove) { - CopySourceInfo copySource= new CopySourceInfo(parent, property, node, node, isMove); + private final CopySourceInfo createCopySourceInfo(PropertyLocation location, ASTNode node, boolean isMove) { + CopySourceInfo copySource= new CopySourceInfo(location, node, isMove); if (this.nodeCopySources == null) { this.nodeCopySources= new ArrayList(); @@ -495,18 +559,47 @@ return copySource; } - public final CopySourceInfo markAsRangeCopySource(ASTNode parent, StructuralPropertyDescriptor property, ASTNode first, ASTNode last, boolean isMove) { - CopySourceInfo copySource= new CopySourceInfo(parent, property, first, last, isMove); - assertNoOverlap(copySource); + public final CopySourceInfo markAsCopySource(ASTNode parent, StructuralPropertyDescriptor property, ASTNode node, boolean isMove) { + return createCopySourceInfo(new PropertyLocation(parent, property), node, isMove); + } + + public final CopySourceInfo createRangeCopy(ASTNode parent, StructuralPropertyDescriptor childProperty, ASTNode first, ASTNode last, boolean isMove, ASTNode internalPlaceholder, ASTNode replacingNode, TextEditGroup editGroup) { + CopySourceInfo copyInfo= createCopySourceInfo(null, internalPlaceholder, isMove); - if (this.rangeCopySources == null) { - this.rangeCopySources= new ArrayList(); + NodeRangeInfo copyRangeInfo= new NodeRangeInfo(parent, childProperty, first, last, copyInfo, replacingNode, editGroup); + + ListRewriteEvent listEvent= getListEvent(parent, childProperty, true); + + int indexFirst= listEvent.getIndex(first, ListRewriteEvent.OLD); + if (indexFirst == -1) { + throw new IllegalArgumentException("Start node is not a original child of the given list"); //$NON-NLS-1$ } - this.rangeCopySources.add(copySource); - return copySource; + int indexLast= listEvent.getIndex(last, ListRewriteEvent.OLD); + if (indexLast == -1) { + throw new IllegalArgumentException("End node is not a original child of the given list"); //$NON-NLS-1$ + } + + if (indexFirst > indexLast) { + throw new IllegalArgumentException("Start node must be before end node"); //$NON-NLS-1$ + } + + if (this.nodeRangeInfos == null) { + this.nodeRangeInfos= new HashMap(); + } + PropertyLocation loc= new PropertyLocation(parent, childProperty); + List innerList= (List) this.nodeRangeInfos.get(loc); + if (innerList == null) { + innerList= new ArrayList(2); + this.nodeRangeInfos.put(loc, innerList); + } else { + assertNoOverlap(listEvent, indexFirst, indexLast, innerList); + } + innerList.add(copyRangeInfo); + + + return copyInfo; } - public CopySourceInfo[] getNodeCopySources(ASTNode node) { if (this.nodeCopySources == null) { return null; @@ -514,105 +607,172 @@ return internalGetCopySources(this.nodeCopySources, node); } - public CopySourceInfo[] getRangeCopySources(ASTNode node) { - if (this.rangeCopySources == null) { - return null; - } - return internalGetCopySources(this.rangeCopySources, node); - } - - public boolean hasRangeCopySources(ASTNode parent, StructuralPropertyDescriptor property) { - if (this.rangeCopySources == null) { - return false; - } - for (int i= 0; i < this.rangeCopySources.size(); i++) { - CopySourceInfo curr= (CopySourceInfo) this.rangeCopySources.get(i); - if (curr.parent == parent && curr.childProperty == property) { - return true; - } - } - return false; - } public CopySourceInfo[] internalGetCopySources(List copySources, ASTNode node) { ArrayList res= new ArrayList(3); for (int i= 0; i < copySources.size(); i++) { CopySourceInfo curr= (CopySourceInfo) copySources.get(i); - if (curr.getStartNode() == node) { + if (curr.getNode() == node) { res.add(curr); } } if (res.isEmpty()) { return null; } + CopySourceInfo[] arr= (CopySourceInfo[]) res.toArray(new CopySourceInfo[res.size()]); - if (arr.length > 1) { - Arrays.sort(arr, new CopySourceInfoSorter()); - } + Arrays.sort(arr); return arr; } - private void assertNoOverlap(CopySourceInfo copySource) { - ASTNode parent= copySource.parent; - StructuralPropertyDescriptor childProperty= copySource.childProperty; - ASTNode first= copySource.getStartNode(); - ASTNode last= copySource.getEndNode(); - - ListRewriteEvent listEvent= getListEvent(parent, childProperty, true); + private void assertNoOverlap(ListRewriteEvent listEvent, int indexFirst, int indexLast, List innerList) { + for (Iterator iter= innerList.iterator(); iter.hasNext();) { + NodeRangeInfo curr= (NodeRangeInfo) iter.next(); + int currStart= listEvent.getIndex(curr.getStartNode(), ListRewriteEvent.BOTH); + int currEnd= listEvent.getIndex(curr.getEndNode(), ListRewriteEvent.BOTH); + if (currStart < indexFirst && currEnd < indexLast && currEnd >= indexFirst + || currStart > indexFirst && currStart <= currEnd && currEnd > indexLast) { + throw new IllegalArgumentException("Range overlapps with an existing copy or move range"); //$NON-NLS-1$ + } + } + } + + public void prepareMovedNodes(TargetSourceRangeComputer sourceRangeComputer) { + if (this.nodeCopySources != null) { + prepareSingleNodeCopies(); + } - int indexFirst= listEvent.getIndex(first, ListRewriteEvent.OLD); - if (indexFirst == -1) { - throw new IllegalArgumentException("Start node is not a original child of the given list"); //$NON-NLS-1$ + if (this.nodeRangeInfos != null) { + prepareNodeRangeCopies(sourceRangeComputer); } - int indexLast= listEvent.getIndex(last, ListRewriteEvent.OLD); - if (indexLast == -1) { - throw new IllegalArgumentException("End node is not a original child of the given list"); //$NON-NLS-1$ + } + + public void revertMovedNodes() { + if (this.nodeRangeInfos != null) { + removeMoveRangePlaceholders(); } - - if (indexFirst > indexLast) { - throw new IllegalArgumentException("Start node must be before end node"); //$NON-NLS-1$ + } + + private void removeMoveRangePlaceholders() { + for (Iterator iter= this.nodeRangeInfos.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry= (Map.Entry) iter.next(); + Set placeholders= new HashSet(); // collect all placeholders + List rangeInfos= (List) entry.getValue(); // list of CopySourceRange + for (int i= 0; i < rangeInfos.size(); i++) { + placeholders.add(((NodeRangeInfo) rangeInfos.get(i)).getInternalPlaceholder()); + } + + PropertyLocation loc= (PropertyLocation) entry.getKey(); + + RewriteEvent[] children= getListEvent(loc.getParent(), loc.getProperty(), true).getChildren(); + List revertedChildren= new ArrayList(); + revertListWithRanges(children, placeholders, revertedChildren); + RewriteEvent[] revertedChildrenArr= (RewriteEvent[]) revertedChildren.toArray(new RewriteEvent[revertedChildren.size()]); + addEvent(loc.getParent(), loc.getProperty(), new ListRewriteEvent(revertedChildrenArr)); // replace the current edits + } + } + + private void revertListWithRanges(RewriteEvent[] childEvents, Set placeholders, List revertedChildren) { + for (int i= 0; i < childEvents.length; i++) { + RewriteEvent event= childEvents[i]; + ASTNode node= (ASTNode) event.getOriginalValue(); + if (placeholders.contains(node)) { + RewriteEvent[] placeholderChildren= getListEvent(node, Block.STATEMENTS_PROPERTY, false).getChildren(); + revertListWithRanges(placeholderChildren, placeholders, revertedChildren); + } else { + revertedChildren.add(event); + } } - if (this.rangeCopySources != null) { - for (Iterator iter= this.rangeCopySources.iterator(); iter.hasNext();) { - CopySourceInfo info= (CopySourceInfo) iter.next(); - if (info.parent == parent && info.childProperty == childProperty) { - int currStart= listEvent.getIndex(first, ListRewriteEvent.BOTH); - int currEnd= listEvent.getIndex(first, ListRewriteEvent.BOTH); - if (currStart < indexFirst && currEnd < indexLast && currEnd >= indexFirst - || currStart > indexFirst && currStart <= currEnd && currEnd > indexLast) { - throw new IllegalArgumentException("Range overlapps with an existing copy or move range"); //$NON-NLS-1$ - } + } + + private void prepareNodeRangeCopies(TargetSourceRangeComputer sourceRangeComputer) { + for (Iterator iter= this.nodeRangeInfos.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry= (Map.Entry) iter.next(); + List rangeInfos= (List) entry.getValue(); // list of CopySourceRange + Collections.sort(rangeInfos); // sort by start index, length, move or copy + + PropertyLocation loc= (PropertyLocation) entry.getKey(); + RewriteEvent[] children= getListEvent(loc.getParent(), loc.getProperty(), true).getChildren(); + + RewriteEvent[] newChildren= processListWithRanges(rangeInfos, children, sourceRangeComputer); + addEvent(loc.getParent(), loc.getProperty(), new ListRewriteEvent(newChildren)); // replace the current edits + } + } + + private RewriteEvent[] processListWithRanges(List rangeInfos, RewriteEvent[] childEvents, TargetSourceRangeComputer sourceRangeComputer) { + List newChildEvents= new ArrayList(childEvents.length); + NodeRangeInfo topInfo= null; + Stack newChildrenStack= new Stack(); + Stack topInfoStack= new Stack(); + + Iterator rangeInfoIterator= rangeInfos.iterator(); + NodeRangeInfo nextInfo= (NodeRangeInfo) rangeInfoIterator.next(); + + for (int k= 0; k < childEvents.length; k++) { + RewriteEvent event= childEvents[k]; + ASTNode node= (ASTNode) event.getOriginalValue(); + // check for ranges and add a placeholder for them + while (nextInfo != null && node == nextInfo.getStartNode()) { // is this child the beginning of a range? + nextInfo.updatePlaceholderSourceRanges(sourceRangeComputer); + + Block internalPlaceholder= nextInfo.getInternalPlaceholder(); + RewriteEvent newEvent; + if (nextInfo.isMove()) { + newEvent= new NodeRewriteEvent(internalPlaceholder, nextInfo.replacingNode); // remove or replace + } else { + newEvent= new NodeRewriteEvent(internalPlaceholder, internalPlaceholder); // unchanged + } + newChildEvents.add(newEvent); + if (nextInfo.editGroup != null) { + setEventEditGroup(newEvent, nextInfo.editGroup); } + + newChildrenStack.push(newChildEvents); + topInfoStack.push(topInfo); + + newChildEvents= new ArrayList(childEvents.length); + topInfo= nextInfo; + + nextInfo= rangeInfoIterator.hasNext() ? (NodeRangeInfo) rangeInfoIterator.next() : null; + } + + newChildEvents.add(event); + + while (topInfo != null && node == topInfo.getEndNode()) { + RewriteEvent[] placeholderChildEvents= (RewriteEvent[]) newChildEvents.toArray(new RewriteEvent[newChildEvents.size()]); + Block internalPlaceholder= topInfo.getInternalPlaceholder(); + addEvent(internalPlaceholder, Block.STATEMENTS_PROPERTY, new ListRewriteEvent(placeholderChildEvents)); + + newChildEvents= (List) newChildrenStack.pop(); + topInfo= (NodeRangeInfo) topInfoStack.pop(); } } + return (RewriteEvent[]) newChildEvents.toArray(new RewriteEvent[newChildEvents.size()]); } - + /** * Make sure all moved nodes are marked as removed or replaced. */ - public void markMovedNodesRemoved() { - if (this.nodeCopySources == null) { - return; - } + private void prepareSingleNodeCopies() { for (int i= 0; i < this.nodeCopySources.size(); i++) { CopySourceInfo curr= (CopySourceInfo) this.nodeCopySources.get(i); - if (curr.isMove) { - doMarkMovedAsRemoved(curr); + if (curr.isMove && curr.location != null) { + doMarkMovedAsRemoved(curr, curr.location.getParent(), curr.location.getProperty()); } } } - private void doMarkMovedAsRemoved(CopySourceInfo curr) { - if (curr.childProperty.isChildListProperty()) { - ListRewriteEvent event= getListEvent(curr.parent, curr.childProperty, true); - int index= event.getIndex(curr.getStartNode(), ListRewriteEvent.OLD); + private void doMarkMovedAsRemoved(CopySourceInfo curr, ASTNode parent, StructuralPropertyDescriptor childProperty) { + if (childProperty.isChildListProperty()) { + ListRewriteEvent event= getListEvent(parent, childProperty, true); + int index= event.getIndex(curr.getNode(), ListRewriteEvent.OLD); if (index != -1 && event.getChangeKind(index) == RewriteEvent.UNCHANGED) { event.setNewValue(null, index); } } else { - NodeRewriteEvent event= getNodeEvent(curr.parent, curr.childProperty, true); + NodeRewriteEvent event= getNodeEvent(parent, childProperty, true); if (event.getChangeKind() == RewriteEvent.UNCHANGED) { event.setNewValue(null); }