/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.core.dom.rewrite; import org.eclipse.che.ide.ext.java.jdt.core.Signature; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.Block; import org.eclipse.che.ide.ext.java.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.TargetSourceRangeComputer; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEditGroup; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; 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 java.util.Stack; /** Stores all rewrite events, descriptions of events and knows which nodes are copy or move sources or tracked. */ public final class RewriteEventStore { public static final class PropertyLocation { private final ASTNode parent; private final StructuralPropertyDescriptor property; public PropertyLocation(ASTNode parent, StructuralPropertyDescriptor property) { this.parent = parent; this.property = property; } public ASTNode getParent() { return this.parent; } public StructuralPropertyDescriptor getProperty() { return this.property; } public boolean equals(Object obj) { if (obj != null && obj.getClass().equals(getClass())) { PropertyLocation other = (PropertyLocation)obj; return other.getParent().equals(getParent()) && other.getProperty().equals(getProperty()); } return false; } public int hashCode() { return getParent().hashCode() + getProperty().hashCode(); } } /** * Interface that allows to override the way how children are accessed from a parent. Use this interface when the rewriter is * set up on an already modified AST's (as it is the case in the old ASTRewrite infrastructure) */ public static interface INodePropertyMapper { /** * Returns the node attribute for a given property name. * * @param parent * The parent node * @param childProperty * The child property to access * @return The child node at the given property location. */ Object getOriginalValue(ASTNode parent, StructuralPropertyDescriptor childProperty); } /* Store element to associate event and node position/ */ private static class EventHolder { public final ASTNode parent; public final StructuralPropertyDescriptor childProperty; public final RewriteEvent event; public EventHolder(ASTNode parent, StructuralPropertyDescriptor childProperty, RewriteEvent change) { this.parent = parent; this.childProperty = childProperty; this.event = change; } public String toString() { StringBuffer buf = new StringBuffer(); buf.append(this.parent).append(" - "); //$NON-NLS-1$ buf.append(this.childProperty.getId()).append(": "); //$NON-NLS-1$ buf.append(this.event).append('\n'); return buf.toString(); } } 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 = 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 CopySourceInfo copyInfo; // containing the internal placeholder and the 'isMove' flag public final ASTNode replacingNode; public final TextEditGroup editGroup; public NodeRangeInfo(ASTNode parent, StructuralPropertyDescriptor childProperty, ASTNode first, ASTNode last, CopySourceInfo copyInfo, ASTNode replacingNode, TextEditGroup editGroup) { this.first = first; this.last = last; this.copyInfo = copyInfo; this.replacingNode = replacingNode; this.editGroup = editGroup; } public ASTNode getStartNode() { return this.first; } 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 = getStartNode().getStartPosition() - r2.getStartNode().getStartPosition(); if (startDiff != 0) { return startDiff; // insert before if start node is first } int endDiff = getEndNode().getStartPosition() - r2.getEndNode().getStartPosition(); if (endDiff != 0) { return -endDiff; // insert before if length is longer } if (r2.isMove() != isMove()) { return 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 (isMove()) { buf.append("move source: "); //$NON-NLS-1$ } else { buf.append("copy source: "); //$NON-NLS-1$ } buf.append(this.first); buf.append(" - "); //$NON-NLS-1$ buf.append(this.last); return buf.toString(); } } /** Iterates over all event parent nodes, tracked nodes and all copy/move sources */ private class ParentIterator implements Iterator { private Iterator eventIter; private Iterator sourceNodeIter; private Iterator rangeNodeIter; private Iterator trackedNodeIter; public ParentIterator() { this.eventIter = RewriteEventStore.this.eventLookup.keySet().iterator(); if (RewriteEventStore.this.nodeCopySources != null) { this.sourceNodeIter = RewriteEventStore.this.nodeCopySources.iterator(); } else { this.sourceNodeIter = Collections.EMPTY_LIST.iterator(); } if (RewriteEventStore.this.nodeRangeInfos != null) { this.rangeNodeIter = RewriteEventStore.this.nodeRangeInfos.keySet().iterator(); } else { this.rangeNodeIter = Collections.EMPTY_LIST.iterator(); } if (RewriteEventStore.this.trackedNodes != null) { this.trackedNodeIter = RewriteEventStore.this.trackedNodes.keySet().iterator(); } else { this.trackedNodeIter = Collections.EMPTY_LIST.iterator(); } } /* * (non-Javadoc) * @see java.util.Iterator#hasNext() */ public boolean hasNext() { return this.eventIter.hasNext() || this.sourceNodeIter.hasNext() || this.rangeNodeIter.hasNext() || this.trackedNodeIter.hasNext(); } /* * (non-Javadoc) * @see java.util.Iterator#next() */ public Object next() { if (this.eventIter.hasNext()) { return this.eventIter.next(); } if (this.sourceNodeIter.hasNext()) { return ((CopySourceInfo)this.sourceNodeIter.next()).getNode(); } if (this.rangeNodeIter.hasNext()) { return ((PropertyLocation)this.rangeNodeIter.next()).getParent(); } return this.trackedNodeIter.next(); } /* * (non-Javadoc) * @see java.util.Iterator#remove() */ public void remove() { throw new UnsupportedOperationException(); } } public final static int NEW = 1; public final static int ORIGINAL = 2; public final static int BOTH = NEW | ORIGINAL; /** all events by parent */ final Map<ASTNode, List<EventHolder>> eventLookup; /** cache for last accessed event */ private EventHolder lastEvent; /** Maps events to group descriptions */ private Map editGroups; /** Stores which nodes are source of a copy or move (list of CopySourceInfo) */ List nodeCopySources; /** Stores node ranges that are used to copy or move (map of <PropertyLocation, CopyRangeInfo>) */ Map nodeRangeInfos; /** Stores which nodes are tracked and the corresponding edit group */ Map trackedNodes; /** Stores which inserted nodes bound to the previous node. If not, a node is always bound to the next node */ private Set insertBoundToPrevious; /** optional mapper to allow fix already modified AST trees */ private INodePropertyMapper nodePropertyMapper; private static final String INTERNAL_PLACEHOLDER_PROPERTY = "rewrite_internal_placeholder"; //$NON-NLS-1$ public RewriteEventStore() { this.eventLookup = new HashMap<ASTNode, List<EventHolder>>(); this.lastEvent = null; this.editGroups = null; // lazy initialization this.trackedNodes = null; this.insertBoundToPrevious = null; this.nodePropertyMapper = null; this.nodeCopySources = null; this.nodeRangeInfos = null; } /** * Override the default way how to access children from a parent node. * * @param nodePropertyMapper * The new <code>INodePropertyMapper</code> or <code>null</code>. to use the default. */ public void setNodePropertyMapper(INodePropertyMapper nodePropertyMapper) { this.nodePropertyMapper = nodePropertyMapper; } public void clear() { this.eventLookup.clear(); this.lastEvent = null; this.trackedNodes = null; this.editGroups = null; // lazy initialization this.insertBoundToPrevious = null; this.nodeCopySources = null; } public void addEvent(ASTNode parent, StructuralPropertyDescriptor childProperty, RewriteEvent event) { validateHasChildProperty(parent, childProperty); if (event.isListRewrite()) { validateIsListProperty(childProperty); } EventHolder holder = new EventHolder(parent, childProperty, event); List<EventHolder> entriesList = this.eventLookup.get(parent); if (entriesList != null) { for (int i = 0; i < entriesList.size(); i++) { EventHolder curr = (EventHolder)entriesList.get(i); if (curr.childProperty == childProperty) { entriesList.set(i, holder); this.lastEvent = null; return; } } } else { entriesList = new ArrayList<RewriteEventStore.EventHolder>(3); this.eventLookup.put(parent, entriesList); } entriesList.add(holder); } public RewriteEvent getEvent(ASTNode parent, StructuralPropertyDescriptor property) { validateHasChildProperty(parent, property); if (this.lastEvent != null && this.lastEvent.parent == parent && this.lastEvent.childProperty == property) { return this.lastEvent.event; } List<EventHolder> entriesList = this.eventLookup.get(parent); if (entriesList != null) { for (int i = 0; i < entriesList.size(); i++) { EventHolder holder = (EventHolder)entriesList.get(i); if (holder.childProperty == property) { this.lastEvent = holder; return holder.event; } } } return null; } public NodeRewriteEvent getNodeEvent(ASTNode parent, StructuralPropertyDescriptor childProperty, boolean forceCreation) { validateIsNodeProperty(childProperty); NodeRewriteEvent event = (NodeRewriteEvent)getEvent(parent, childProperty); if (event == null && forceCreation) { Object originalValue = accessOriginalValue(parent, childProperty); event = new NodeRewriteEvent(originalValue, originalValue); addEvent(parent, childProperty, event); } return event; } public ListRewriteEvent getListEvent(ASTNode parent, StructuralPropertyDescriptor childProperty, boolean forceCreation) { validateIsListProperty(childProperty); ListRewriteEvent event = (ListRewriteEvent)getEvent(parent, childProperty); if (event == null && forceCreation) { List originalValue = (List)accessOriginalValue(parent, childProperty); event = new ListRewriteEvent(originalValue); addEvent(parent, childProperty, event); } return event; } public Iterator<?> getChangeRootIterator() { return new ParentIterator(); } public boolean hasChangedProperties(ASTNode parent) { List entriesList = (List)this.eventLookup.get(parent); if (entriesList != null) { for (int i = 0; i < entriesList.size(); i++) { EventHolder holder = (EventHolder)entriesList.get(i); if (holder.event.getChangeKind() != RewriteEvent.UNCHANGED) { return true; } } } return false; } public PropertyLocation getPropertyLocation(Object value, int kind) { for (Iterator iter = this.eventLookup.values().iterator(); iter.hasNext(); ) { List events = (List)iter.next(); for (int i = 0; i < events.size(); i++) { EventHolder holder = (EventHolder)events.get(i); RewriteEvent event = holder.event; if (isNodeInEvent(event, value, kind)) { return new PropertyLocation(holder.parent, holder.childProperty); } if (event.isListRewrite()) { RewriteEvent[] children = event.getChildren(); for (int k = 0; k < children.length; k++) { if (isNodeInEvent(children[k], value, kind)) { return new PropertyLocation(holder.parent, holder.childProperty); } } } } } if (value instanceof ASTNode) { ASTNode node = (ASTNode)value; return new PropertyLocation(node.getParent(), node.getLocationInParent()); } return null; } /** * Kind is either ORIGINAL, NEW, or BOTH * * @param value * @param kind * @return Returns the event with the given value of <code>null</code>. */ public RewriteEvent findEvent(Object value, int kind) { for (Iterator iter = this.eventLookup.values().iterator(); iter.hasNext(); ) { List events = (List)iter.next(); for (int i = 0; i < events.size(); i++) { RewriteEvent event = ((EventHolder)events.get(i)).event; if (isNodeInEvent(event, value, kind)) { return event; } if (event.isListRewrite()) { RewriteEvent[] children = event.getChildren(); for (int k = 0; k < children.length; k++) { if (isNodeInEvent(children[k], value, kind)) { return children[k]; } } } } } return null; } private boolean isNodeInEvent(RewriteEvent event, Object value, int kind) { if (((kind & NEW) != 0) && event.getNewValue() == value) { return true; } if (((kind & ORIGINAL) != 0) && event.getOriginalValue() == value) { return true; } return false; } public Object getOriginalValue(ASTNode parent, StructuralPropertyDescriptor property) { RewriteEvent event = getEvent(parent, property); if (event != null) { return event.getOriginalValue(); } return accessOriginalValue(parent, property); } public Object getNewValue(ASTNode parent, StructuralPropertyDescriptor property) { RewriteEvent event = getEvent(parent, property); if (event != null) { return event.getNewValue(); } return accessOriginalValue(parent, property); } public List getChangedPropertieEvents(ASTNode parent) { List<RewriteEvent> changedPropertiesEvent = new ArrayList<RewriteEvent>(); List<EventHolder> entriesList = (List)this.eventLookup.get(parent); if (entriesList != null) { for (int i = 0; i < entriesList.size(); i++) { EventHolder holder = (EventHolder)entriesList.get(i); if (holder.event.getChangeKind() != RewriteEvent.UNCHANGED) { changedPropertiesEvent.add(holder.event); } } } return changedPropertiesEvent; } public int getChangeKind(ASTNode node) { RewriteEvent event = findEvent(node, ORIGINAL); if (event != null) { return event.getChangeKind(); } return RewriteEvent.UNCHANGED; } /* * Gets an original child from the AST. Temporarily overridden to port the old rewriter to the new infrastructure. */ private Object accessOriginalValue(ASTNode parent, StructuralPropertyDescriptor childProperty) { if (this.nodePropertyMapper != null) { return this.nodePropertyMapper.getOriginalValue(parent, childProperty); } return parent.getStructuralProperty(childProperty); } public TextEditGroup getEventEditGroup(RewriteEvent event) { if (this.editGroups == null) { return null; } return (TextEditGroup)this.editGroups.get(event); } public void setEventEditGroup(RewriteEvent event, TextEditGroup editGroup) { if (this.editGroups == null) { this.editGroups = new IdentityHashMap(5); } this.editGroups.put(event, editGroup); } public final TextEditGroup getTrackedNodeData(ASTNode node) { if (this.trackedNodes != null) { return (TextEditGroup)this.trackedNodes.get(node); } return null; } public void setTrackedNodeData(ASTNode node, TextEditGroup editGroup) { if (this.trackedNodes == null) { this.trackedNodes = new IdentityHashMap(); } this.trackedNodes.put(node, editGroup); } /** * Marks a node as tracked. The edits added to the group editGroup can be used to get the position of the node after the * rewrite operation. * * @param node * The node to track * @param editGroup * Collects the range markers describing the node position. */ public final void markAsTracked(ASTNode node, TextEditGroup editGroup) { if (getTrackedNodeData(node) != null) { throw new IllegalArgumentException("Node is already marked as tracked"); //$NON-NLS-1$ } setTrackedNodeData(node, editGroup); } 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(); } this.nodeCopySources.add(copySource); return copySource; } public final CopySourceInfo markAsCopySource(ASTNode parent, StructuralPropertyDescriptor property, ASTNode node, boolean isMove) { return createCopySourceInfo(new PropertyLocation(parent, property), node, isMove); } public final boolean isRangeCopyPlaceholder(ASTNode node) { return node.getProperty(INTERNAL_PLACEHOLDER_PROPERTY) != null; } 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); internalPlaceholder.setProperty(INTERNAL_PLACEHOLDER_PROPERTY, internalPlaceholder); 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$ } 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; } return internalGetCopySources(this.nodeCopySources, node); } 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.getNode() == node) { res.add(curr); } } if (res.isEmpty()) { return null; } CopySourceInfo[] arr = (CopySourceInfo[])res.toArray(new CopySourceInfo[res.size()]); Arrays.sort(arr); return arr; } 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(); } if (this.nodeRangeInfos != null) { prepareNodeRangeCopies(sourceRangeComputer); } } public void revertMovedNodes() { if (this.nodeRangeInfos != null) { removeMoveRangePlaceholders(); } } 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); } } } 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. */ private void prepareSingleNodeCopies() { for (int i = 0; i < this.nodeCopySources.size(); i++) { CopySourceInfo curr = (CopySourceInfo)this.nodeCopySources.get(i); if (curr.isMove && curr.location != null) { doMarkMovedAsRemoved(curr, curr.location.getParent(), curr.location.getProperty()); } } } 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(parent, childProperty, true); if (event.getChangeKind() == RewriteEvent.UNCHANGED) { event.setNewValue(null); } } } public boolean isInsertBoundToPrevious(ASTNode node) { if (this.insertBoundToPrevious != null) { return this.insertBoundToPrevious.contains(node); } return false; } public void setInsertBoundToPrevious(ASTNode node) { if (this.insertBoundToPrevious == null) { this.insertBoundToPrevious = new HashSet(); } this.insertBoundToPrevious.add(node); } private void validateIsListProperty(StructuralPropertyDescriptor property) { if (!property.isChildListProperty()) { String message = property.getId() + " is not a list property"; //$NON-NLS-1$ throw new IllegalArgumentException(message); } } private void validateHasChildProperty(ASTNode parent, StructuralPropertyDescriptor property) { if (!parent.structuralPropertiesForType().contains(property)) { String message = Signature.getSimpleName(parent.getClass().getName()) + " has no property " + property.getId(); //$NON-NLS-1$ throw new IllegalArgumentException(message); } } private void validateIsNodeProperty(StructuralPropertyDescriptor property) { if (property.isChildListProperty()) { String message = property.getId() + " is not a node property"; //$NON-NLS-1$ throw new IllegalArgumentException(message); } } public String toString() { StringBuffer buf = new StringBuffer(); for (Iterator iter = this.eventLookup.values().iterator(); iter.hasNext(); ) { List events = (List)iter.next(); for (int i = 0; i < events.size(); i++) { buf.append(events.get(i).toString()).append('\n'); } } return buf.toString(); } public static boolean isNewNode(ASTNode node) { return (node.getFlags() & ASTNode.ORIGINAL) == 0; } }