/*******************************************************************************
* 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;
}
}