001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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.text.edits;
011:
012: import java.util.ArrayList;
013: import java.util.List;
014:
015: import org.eclipse.core.runtime.Assert;
016:
017: import org.eclipse.jface.text.BadLocationException;
018: import org.eclipse.jface.text.IDocument;
019:
020: /**
021: * A copy source edit denotes the source of a copy operation. Copy
022: * source edits are only valid inside an edit tree if they have a
023: * corresponding target edit. Furthermore the corresponding
024: * target edit can't be a direct or indirect child of the source
025: * edit. Violating one of two requirements will result in a <code>
026: * MalformedTreeException</code> when executing the edit tree.
027: * <p>
028: * A copy source edit can manage an optional source modifier. A
029: * source modifier can provide a set of replace edits which will
030: * to applied to the source before it gets inserted at the target
031: * position.
032: *
033: * @see org.eclipse.text.edits.CopyTargetEdit
034: *
035: * @since 3.0
036: */
037: public final class CopySourceEdit extends TextEdit {
038:
039: private CopyTargetEdit fTarget;
040: private ISourceModifier fModifier;
041:
042: private String fSourceContent;
043: private TextEdit fSourceRoot;
044:
045: private static class PartialCopier extends TextEditVisitor {
046: TextEdit fResult;
047: List fParents = new ArrayList();
048: TextEdit fCurrentParent;
049:
050: public static TextEdit perform(TextEdit source) {
051: PartialCopier copier = new PartialCopier();
052: source.accept(copier);
053: return copier.fResult;
054: }
055:
056: private void manageCopy(TextEdit copy) {
057: if (fResult == null)
058: fResult = copy;
059: if (fCurrentParent != null) {
060: fCurrentParent.addChild(copy);
061: }
062: fParents.add(fCurrentParent);
063: fCurrentParent = copy;
064: }
065:
066: public void postVisit(TextEdit edit) {
067: fCurrentParent = (TextEdit) fParents
068: .remove(fParents.size() - 1);
069: }
070:
071: public boolean visitNode(TextEdit edit) {
072: manageCopy(edit.doCopy());
073: return true;
074: }
075:
076: public boolean visit(CopySourceEdit edit) {
077: manageCopy(new RangeMarker(edit.getOffset(), edit
078: .getLength()));
079: return true;
080: }
081:
082: public boolean visit(CopyTargetEdit edit) {
083: manageCopy(new InsertEdit(edit.getOffset(), edit
084: .getSourceEdit().getContent()));
085: return true;
086: }
087:
088: public boolean visit(MoveSourceEdit edit) {
089: manageCopy(new DeleteEdit(edit.getOffset(), edit
090: .getLength()));
091: return true;
092: }
093:
094: public boolean visit(MoveTargetEdit edit) {
095: manageCopy(new InsertEdit(edit.getOffset(), edit
096: .getSourceEdit().getContent()));
097: return true;
098: }
099: }
100:
101: /**
102: * Constructs a new copy source edit.
103: *
104: * @param offset the edit's offset
105: * @param length the edit's length
106: */
107: public CopySourceEdit(int offset, int length) {
108: super (offset, length);
109: }
110:
111: /**
112: * Constructs a new copy source edit.
113: *
114: * @param offset the edit's offset
115: * @param length the edit's length
116: * @param target the edit's target
117: */
118: public CopySourceEdit(int offset, int length, CopyTargetEdit target) {
119: this (offset, length);
120: setTargetEdit(target);
121: }
122:
123: /*
124: * Copy Constructor
125: */
126: private CopySourceEdit(CopySourceEdit other) {
127: super (other);
128: if (other.fModifier != null)
129: fModifier = other.fModifier.copy();
130: }
131:
132: /**
133: * Returns the associated target edit or <code>null</code>
134: * if no target edit is associated yet.
135: *
136: * @return the target edit or <code>null</code>
137: */
138: public CopyTargetEdit getTargetEdit() {
139: return fTarget;
140: }
141:
142: /**
143: * Sets the target edit.
144: *
145: * @param edit the new target edit.
146: *
147: * @exception MalformedTreeException is thrown if the target edit
148: * is a direct or indirect child of the source edit
149: */
150: public void setTargetEdit(CopyTargetEdit edit)
151: throws MalformedTreeException {
152: Assert.isNotNull(edit);
153: if (fTarget != edit) {
154: fTarget = edit;
155: fTarget.setSourceEdit(this );
156: }
157: }
158:
159: /**
160: * Returns the current source modifier or <code>null</code>
161: * if no source modifier is set.
162: *
163: * @return the source modifier
164: */
165: public ISourceModifier getSourceModifier() {
166: return fModifier;
167: }
168:
169: /**
170: * Sets the optional source modifier.
171: *
172: * @param modifier the source modifier or <code>null</code>
173: * if no source modification is need.
174: */
175: public void setSourceModifier(ISourceModifier modifier) {
176: fModifier = modifier;
177: }
178:
179: /*
180: * @see TextEdit#doCopy
181: */
182: protected TextEdit doCopy() {
183: return new CopySourceEdit(this );
184: }
185:
186: /*
187: * @see TextEdit#accept0
188: */
189: protected void accept0(TextEditVisitor visitor) {
190: boolean visitChildren = visitor.visit(this );
191: if (visitChildren) {
192: acceptChildren(visitor);
193: }
194: }
195:
196: //---- API for CopyTargetEdit ------------------------------------------------
197:
198: String getContent() {
199: // The source content can be null if the edit wasn't executed
200: // due to an exclusion list of the text edit processor. Return
201: // the empty string which can be moved without any harm.
202: if (fSourceContent == null)
203: return ""; //$NON-NLS-1$
204: return fSourceContent;
205: }
206:
207: void clearContent() {
208: fSourceContent = null;
209: }
210:
211: /*
212: * @see TextEdit#postProcessCopy
213: */
214: protected void postProcessCopy(TextEditCopier copier) {
215: if (fTarget != null) {
216: CopySourceEdit source = (CopySourceEdit) copier
217: .getCopy(this );
218: CopyTargetEdit target = (CopyTargetEdit) copier
219: .getCopy(fTarget);
220: if (source != null && target != null)
221: source.setTargetEdit(target);
222: }
223: }
224:
225: //---- consistency check ----------------------------------------------------
226:
227: int traverseConsistencyCheck(TextEditProcessor processor,
228: IDocument document, List sourceEdits) {
229: int result = super .traverseConsistencyCheck(processor,
230: document, sourceEdits);
231: // Since source computation takes place in a recursive fashion (see
232: // performSourceComputation) we only do something if we don't have a
233: // computed source already.
234: if (fSourceContent == null) {
235: if (sourceEdits.size() <= result) {
236: List list = new ArrayList();
237: list.add(this );
238: for (int i = sourceEdits.size(); i < result; i++)
239: sourceEdits.add(null);
240: sourceEdits.add(list);
241: } else {
242: List list = (List) sourceEdits.get(result);
243: if (list == null) {
244: list = new ArrayList();
245: sourceEdits.add(result, list);
246: }
247: list.add(this );
248: }
249: }
250: return result;
251: }
252:
253: void performConsistencyCheck(TextEditProcessor processor,
254: IDocument document) throws MalformedTreeException {
255: if (fTarget == null)
256: throw new MalformedTreeException(getParent(), this ,
257: TextEditMessages
258: .getString("CopySourceEdit.no_target")); //$NON-NLS-1$
259: if (fTarget.getSourceEdit() != this )
260: throw new MalformedTreeException(
261: getParent(),
262: this ,
263: TextEditMessages
264: .getString("CopySourceEdit.different_source")); //$NON-NLS-1$
265: /* causes ASTRewrite to fail
266: if (getRoot() != fTarget.getRoot())
267: throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("CopySourceEdit.different_tree")); //$NON-NLS-1$
268: */
269: }
270:
271: //---- source computation -------------------------------------------------------
272:
273: void traverseSourceComputation(TextEditProcessor processor,
274: IDocument document) {
275: // always perform source computation independent of processor.considerEdit
276: // The target might need the source and the source is computed in a
277: // temporary buffer.
278: performSourceComputation(processor, document);
279: }
280:
281: void performSourceComputation(TextEditProcessor processor,
282: IDocument document) {
283: try {
284: MultiTextEdit root = new MultiTextEdit(getOffset(),
285: getLength());
286: root.internalSetChildren(internalGetChildren());
287: fSourceContent = document.get(getOffset(), getLength());
288: fSourceRoot = PartialCopier.perform(root);
289: fSourceRoot.internalMoveTree(-getOffset());
290: if (fSourceRoot.hasChildren()) {
291: EditDocument subDocument = new EditDocument(
292: fSourceContent);
293: TextEditProcessor subProcessor = TextEditProcessor
294: .createSourceComputationProcessor(subDocument,
295: fSourceRoot, TextEdit.NONE);
296: subProcessor.performEdits();
297: if (needsTransformation())
298: applyTransformation(subDocument);
299: fSourceContent = subDocument.get();
300: fSourceRoot = null;
301: } else {
302: if (needsTransformation()) {
303: EditDocument subDocument = new EditDocument(
304: fSourceContent);
305: applyTransformation(subDocument);
306: fSourceContent = subDocument.get();
307: }
308: }
309: } catch (BadLocationException cannotHappen) {
310: Assert.isTrue(false);
311: }
312: }
313:
314: private boolean needsTransformation() {
315: return fModifier != null;
316: }
317:
318: private void applyTransformation(IDocument document)
319: throws MalformedTreeException {
320: TextEdit newEdit = new MultiTextEdit(0, document.getLength());
321: ReplaceEdit[] replaces = fModifier.getModifications(document
322: .get());
323: for (int i = 0; i < replaces.length; i++) {
324: newEdit.addChild(replaces[i]);
325: }
326: try {
327: newEdit.apply(document, TextEdit.NONE);
328: } catch (BadLocationException cannotHappen) {
329: Assert.isTrue(false);
330: }
331: }
332:
333: //---- document updating ----------------------------------------------------------------
334:
335: int performDocumentUpdating(IDocument document)
336: throws BadLocationException {
337: fDelta = 0;
338: return fDelta;
339: }
340:
341: //---- region updating ----------------------------------------------------------------
342:
343: /*
344: * @see TextEdit#deleteChildren
345: */
346: boolean deleteChildren() {
347: return false;
348: }
349: }
|