001: /*******************************************************************************
002: * Copyright (c) 2004, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.core.dom.rewrite;
011:
012: import java.util.Collections;
013: import java.util.List;
014:
015: import org.eclipse.jdt.core.dom.ASTNode;
016: import org.eclipse.jdt.core.dom.Block;
017: import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
018: import org.eclipse.jdt.core.dom.FieldDeclaration;
019: import org.eclipse.jdt.core.dom.Statement;
020: import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
021: import org.eclipse.jdt.internal.core.dom.rewrite.ListRewriteEvent;
022: import org.eclipse.jdt.internal.core.dom.rewrite.NodeInfoStore;
023: import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
024: import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore;
025: import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore.CopySourceInfo;
026: import org.eclipse.text.edits.TextEditGroup;
027:
028: /**
029: * For describing manipulations to a child list property of an AST node.
030: * <p>
031: * This class is not intended to be subclassed.
032: * </p>
033: * @see ASTRewrite#getListRewrite(ASTNode, ChildListPropertyDescriptor)
034: * @since 3.0
035: */
036: public final class ListRewrite {
037:
038: private ASTNode parent;
039: private StructuralPropertyDescriptor childProperty;
040: private ASTRewrite rewriter;
041:
042: /* package*/ListRewrite(ASTRewrite rewriter, ASTNode parent,
043: StructuralPropertyDescriptor childProperty) {
044: this .rewriter = rewriter;
045: this .parent = parent;
046: this .childProperty = childProperty;
047: }
048:
049: private RewriteEventStore getRewriteStore() {
050: return this .rewriter.getRewriteEventStore();
051: }
052:
053: private ListRewriteEvent getEvent() {
054: return getRewriteStore().getListEvent(this .parent,
055: this .childProperty, true);
056: }
057:
058: /**
059: * Returns the parent of the list for which this list rewriter was created.
060:
061: * @return the node that contains the list for which this list rewriter was created
062: * @see #getLocationInParent()
063: * @since 3.1
064: */
065: public ASTNode getParent() {
066: return this .parent;
067: }
068:
069: /**
070: * Returns the property of the parent node for which this list rewriter was created.
071: *
072: * @return the property of the parent node for which this list rewriter was created
073: * @see #getParent()
074: * @since 3.1
075: */
076: public StructuralPropertyDescriptor getLocationInParent() {
077: return this .childProperty;
078: }
079:
080: /**
081: * Removes the given node from its parent's list property in the rewriter.
082: * The node must be contained in the list.
083: * The AST itself is not actually modified in any way; rather, the rewriter
084: * just records a note that this node has been removed from this list.
085: *
086: * @param node the node being removed
087: * @param editGroup the edit group in which to collect the corresponding
088: * text edits, or <code>null</code> if ungrouped
089: * @throws IllegalArgumentException if the node is null, or if the node is not
090: * part of this rewriter's AST, or if the described modification is invalid
091: * (not a member of this node's original list)
092: */
093: public void remove(ASTNode node, TextEditGroup editGroup) {
094: if (node == null) {
095: throw new IllegalArgumentException();
096: }
097: RewriteEvent event = getEvent().removeEntry(node);
098: if (editGroup != null) {
099: getRewriteStore().setEventEditGroup(event, editGroup);
100: }
101: }
102:
103: /**
104: * Returns the ASTRewrite instance from which this ListRewriter has been created from.
105: * @return the parent AST Rewriter instance.
106: * @since 3.1
107: */
108: public ASTRewrite getASTRewrite() {
109: return this .rewriter;
110: }
111:
112: /**
113: * Replaces the given node from its parent's list property in the rewriter.
114: * The node must be contained in the list.
115: * The replacement node must either be brand new (not part of the original AST)
116: * or a placeholder node (for example, one created by
117: * {@link ASTRewrite#createCopyTarget(ASTNode)},
118: * {@link ASTRewrite#createMoveTarget(ASTNode)},
119: * or {@link ASTRewrite#createStringPlaceholder(String, int)}). The AST itself
120: * is not actually modified in any way; rather, the rewriter just records
121: * a note that this node has been replaced in this list.
122: *
123: * @param node the node being replaced
124: * @param replacement the replacement node, or <code>null</code> if no
125: * replacement
126: * @param editGroup the edit group in which to collect the corresponding
127: * text edits, or <code>null</code> if ungrouped
128: * @throws IllegalArgumentException if the node is null, or if the node is not part
129: * of this rewriter's AST, or if the replacement node is not a new node (or
130: * placeholder), or if the described modification is otherwise invalid
131: * (not a member of this node's original list)
132: */
133: public void replace(ASTNode node, ASTNode replacement,
134: TextEditGroup editGroup) {
135: if (node == null) {
136: throw new IllegalArgumentException();
137: }
138: RewriteEvent event = getEvent().replaceEntry(node, replacement);
139: if (editGroup != null) {
140: getRewriteStore().setEventEditGroup(event, editGroup);
141: }
142: }
143:
144: /**
145: * Inserts the given node into the list after the given element.
146: * The existing node must be in the list, either as an original or as a new
147: * node that has been inserted.
148: * The inserted node must either be brand new (not part of the original AST)
149: * or a placeholder node (for example, one created by
150: * {@link ASTRewrite#createCopyTarget(ASTNode)},
151: * {@link ASTRewrite#createMoveTarget(ASTNode)},
152: * or {@link ASTRewrite#createStringPlaceholder(String, int)}). The AST itself
153: * is not actually modified in any way; rather, the rewriter just records
154: * a note that this node has been inserted into the list.
155: *
156: * @param node the node to insert
157: * @param element the element after which the given node is to be inserted
158: * @param editGroup the edit group in which to collect the corresponding
159: * text edits, or <code>null</code> if ungrouped
160: * @throws IllegalArgumentException if the node or element is null,
161: * or if the node is not part of this rewriter's AST, or if the inserted node
162: * is not a new node (or placeholder), or if <code>element</code> is not a member
163: * of the list (original or new), or if the described modification is
164: * otherwise invalid
165: */
166: public void insertAfter(ASTNode node, ASTNode element,
167: TextEditGroup editGroup) {
168: if (node == null || element == null) {
169: throw new IllegalArgumentException();
170: }
171: int index = getEvent().getIndex(element, ListRewriteEvent.BOTH);
172: if (index == -1) {
173: throw new IllegalArgumentException("Node does not exist"); //$NON-NLS-1$
174: }
175: internalInsertAt(node, index + 1, true, editGroup);
176: }
177:
178: /**
179: * Inserts the given node into the list before the given element.
180: * The existing node must be in the list, either as an original or as a new
181: * node that has been inserted.
182: * The inserted node must either be brand new (not part of the original AST)
183: * or a placeholder node (for example, one created by
184: * {@link ASTRewrite#createCopyTarget(ASTNode)},
185: * {@link ASTRewrite#createMoveTarget(ASTNode)},
186: * or {@link ASTRewrite#createStringPlaceholder(String, int)}). The AST itself
187: * is not actually modified in any way; rather, the rewriter just records
188: * a note that this node has been inserted into the list.
189: *
190: * @param node the node to insert
191: * @param element the element before which the given node is to be inserted
192: * @param editGroup the edit group in which to collect the corresponding
193: * text edits, or <code>null</code> if ungrouped
194: * @throws IllegalArgumentException if the node or element is null,
195: * or if the node is not part of this rewriter's AST, or if the inserted node
196: * is not a new node (or placeholder), or if <code>element</code> is not a member
197: * of the list (original or new), or if the described modification is
198: * otherwise invalid
199: */
200: public void insertBefore(ASTNode node, ASTNode element,
201: TextEditGroup editGroup) {
202: if (node == null || element == null) {
203: throw new IllegalArgumentException();
204: }
205: int index = getEvent().getIndex(element, ListRewriteEvent.BOTH);
206: if (index == -1) {
207: throw new IllegalArgumentException("Node does not exist"); //$NON-NLS-1$
208: }
209: internalInsertAt(node, index, false, editGroup);
210: }
211:
212: /**
213: * Inserts the given node into the list at the start of the list.
214: * Equivalent to <code>insertAt(node, 0, editGroup)</code>.
215: *
216: * @param node the node to insert
217: * @param editGroup the edit group in which to collect the corresponding
218: * text edits, or <code>null</code> if ungrouped
219: * @throws IllegalArgumentException if the node is null, or if the node is not part
220: * of this rewriter's AST, or if the inserted node is not a new node (or
221: * placeholder), or if the described modification is otherwise invalid
222: * (not a member of this node's original list)
223: * @see #insertAt(ASTNode, int, TextEditGroup)
224: */
225: public void insertFirst(ASTNode node, TextEditGroup editGroup) {
226: if (node == null) {
227: throw new IllegalArgumentException();
228: }
229: internalInsertAt(node, 0, false, editGroup);
230: }
231:
232: /**
233: * Inserts the given node into the list at the end of the list.
234: * Equivalent to <code>insertAt(node, -1, editGroup)</code>.
235: *
236: * @param node the node to insert
237: * @param editGroup the edit group in which to collect the corresponding
238: * text edits, or <code>null</code> if ungrouped
239: * @throws IllegalArgumentException if the node is null, or if the node is not part
240: * of this rewriter's AST, or if the inserted node is not a new node (or
241: * placeholder), or if the described modification is otherwise invalid
242: * (not a member of this node's original list)
243: * @see #insertAt(ASTNode, int, TextEditGroup)
244: */
245: public void insertLast(ASTNode node, TextEditGroup editGroup) {
246: if (node == null) {
247: throw new IllegalArgumentException();
248: }
249: internalInsertAt(node, -1, true, editGroup);
250: }
251:
252: /**
253: * Inserts the given node into the list at the given index.
254: * The index corresponds to a combined list of original and new nodes;
255: * removed or replaced nodes are still in the combined list.
256: * The inserted node must either be brand new (not part of the original AST)
257: * or a placeholder node (for example, one created by
258: * {@link ASTRewrite#createCopyTarget(ASTNode)},
259: * {@link ASTRewrite#createMoveTarget(ASTNode)},
260: * or {@link ASTRewrite#createStringPlaceholder(String, int)}). The AST itself
261: * is not actually modified in any way; rather, the rewriter just records
262: * a note that this node has been inserted into the list.
263: *
264: * @param node the node to insert
265: * @param index insertion index in the combined list of original and
266: * inserted nodes; <code>-1</code> indicates insertion as the last element
267: * @param editGroup the edit group in which to collect the corresponding
268: * text edits, or <code>null</code> if ungrouped
269: * @throws IllegalArgumentException if the node is null, or if the node is not part
270: * of this rewriter's AST, or if the inserted node is not a new node (or
271: * placeholder), or if the described modification is otherwise invalid
272: * (not a member of this node's original list)
273: * @throws IndexOutOfBoundsException if the index is negative and not -1,
274: * or if it is larger than the size of the combined list
275: */
276: public void insertAt(ASTNode node, int index,
277: TextEditGroup editGroup) {
278: if (node == null) {
279: throw new IllegalArgumentException();
280: }
281: internalInsertAt(node, index,
282: isInsertBoundToPreviousByDefault(node), editGroup);
283: }
284:
285: private void internalInsertAt(ASTNode node, int index,
286: boolean boundToPrevious, TextEditGroup editGroup) {
287: RewriteEvent event = getEvent().insert(node, index);
288: if (boundToPrevious) {
289: getRewriteStore().setInsertBoundToPrevious(node);
290: }
291: if (editGroup != null) {
292: getRewriteStore().setEventEditGroup(event, editGroup);
293: }
294: }
295:
296: private ASTNode createTargetNode(ASTNode first, ASTNode last,
297: boolean isMove, ASTNode replacingNode,
298: TextEditGroup editGroup) {
299: if (first == null || last == null) {
300: throw new IllegalArgumentException();
301: }
302:
303: NodeInfoStore nodeStore = this .rewriter.getNodeStore();
304: ASTNode placeholder = nodeStore.newPlaceholderNode(first
305: .getNodeType()); // revisit: could use list type
306: if (placeholder == null) {
307: throw new IllegalArgumentException(
308: "Creating a target node is not supported for nodes of type" + first.getClass().getName()); //$NON-NLS-1$
309: }
310:
311: Block internalPlaceHolder = nodeStore
312: .createCollapsePlaceholder();
313: CopySourceInfo info = getRewriteStore().createRangeCopy(
314: this .parent, this .childProperty, first, last, isMove,
315: internalPlaceHolder, replacingNode, editGroup);
316: nodeStore.markAsCopyTarget(placeholder, info);
317:
318: return placeholder;
319: }
320:
321: /**
322: * Creates and returns a placeholder node for a true copy of a range of nodes of the
323: * current list.
324: * The placeholder node can either be inserted as new or used to replace an
325: * existing node. When the document is rewritten, a copy of the source code
326: * for the given node range is inserted into the output document at the position
327: * corresponding to the placeholder (indentation is adjusted).
328: *
329: * @param first the node that starts the range
330: * @param last the node that ends the range
331: * @return the new placeholder node
332: * @throws IllegalArgumentException An exception is thrown if the first or last node
333: * are <code>null</code>, if a node is not a child of the current list or if the first node
334: * is not before the last node. An <code>IllegalArgumentException</code> is
335: * also thrown if the copied range is overlapping with an other moved or copied range.
336: */
337: public final ASTNode createCopyTarget(ASTNode first, ASTNode last) {
338: if (first == last) {
339: return this .rewriter.createCopyTarget(first);
340: } else {
341: return createTargetNode(first, last, false, null, null);
342: }
343: }
344:
345: /**
346: * Creates and returns a placeholder node for a move of a range of nodes of the
347: * current list.
348: * The placeholder node can either be inserted as new or used to replace an
349: * existing node. When the document is rewritten, a copy of the source code
350: * for the given node range is inserted into the output document at the position
351: * corresponding to the placeholder (indentation is adjusted).
352: *
353: * @param first the node that starts the range
354: * @param last the node that ends the range
355: * @return the new placeholder node
356: * @throws IllegalArgumentException An exception is thrown if the first or last node
357: * are <code>null</code>, if a node is not a child of the current list or if the first node
358: * is not before the last node. An <code>IllegalArgumentException</code> is
359: * also thrown if the moved range is overlapping with an other moved or copied range.
360: *
361: * @since 3.1
362: */
363: public final ASTNode createMoveTarget(ASTNode first, ASTNode last) {
364: return createMoveTarget(first, last, null, null);
365: }
366:
367: /**
368: * Creates and returns a placeholder node for a move of a range of nodes of the
369: * current list. The moved nodes can optionally be replaced by a specified node.
370: *
371: * The placeholder node can either be inserted as new or used to replace an
372: * existing node. When the document is rewritten, a copy of the source code
373: * for the given node range is inserted into the output document at the position
374: * corresponding to the placeholder (indentation is adjusted).
375: *
376: * @param first the node that starts the range
377: * @param last the node that ends the range
378: * @param replacingNode a node that is set at the location of the moved nodes
379: * or <code>null</code> to remove all nodes
380: * @param editGroup the edit group in which to collect the corresponding
381: * text edits fro a replace, or <code>null</code> if ungrouped
382: * @return the new placeholder node
383: * @throws IllegalArgumentException An exception is thrown if the first or
384: * last node are <code>null</code>, if a node is not a child of the current list or
385: * if the first node is not before the last node. An <code>IllegalArgumentException
386: * </code> is also thrown if the moved range is overlapping with an other moved
387: * or copied range.
388: *
389: * @since 3.1
390: */
391: public final ASTNode createMoveTarget(ASTNode first, ASTNode last,
392: ASTNode replacingNode, TextEditGroup editGroup) {
393: if (first == last) {
394: replace(first, replacingNode, editGroup);
395: return this .rewriter.createMoveTarget(first);
396: } else {
397: return createTargetNode(first, last, true, replacingNode,
398: editGroup);
399: }
400: }
401:
402: /*
403: * Heuristic to decide if a inserted node is bound to previous or the next sibling.
404: */
405: private boolean isInsertBoundToPreviousByDefault(ASTNode node) {
406: return (node instanceof Statement || node instanceof FieldDeclaration);
407: }
408:
409: /**
410: * Returns the original nodes in the list property managed by this
411: * rewriter. The returned list is unmodifiable.
412: *
413: * @return a list of all original nodes in the list
414: */
415: public List getOriginalList() {
416: List list = (List) getEvent().getOriginalValue();
417: return Collections.unmodifiableList(list);
418: }
419:
420: /**
421: * Returns the nodes in the revised list property managed by this
422: * rewriter. The returned list is unmodifiable.
423: *
424: * @return a list of all nodes in the list taking into account
425: * all the described changes
426: */
427: public List getRewrittenList() {
428: List list = (List) getEvent().getNewValue();
429: return Collections.unmodifiableList(list);
430: }
431:
432: }
|