| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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.text.edits; |
| |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.Assert; |
| |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| |
| /** |
| * A multi-text edit can be used to aggregate several edits into |
| * one edit. The edit itself doesn't modify a document. |
| * <p> |
| * Clients are allowed to implement subclasses of a multi-text |
| * edit.Subclasses must implement <code>doCopy()</code> to ensure |
| * the a copy of the right type is created. Not implementing |
| * <code>doCopy()</code> in subclasses will result in an assertion |
| * failure during copying. |
| * |
| * @since 3.0 |
| */ |
| public class MultiTextEdit extends TextEdit { |
| |
| private boolean fDefined; |
| |
| /** |
| * Creates a new <code>MultiTextEdit</code>. The range |
| * of the edit is determined by the range of its children. |
| * |
| * Adding this edit to a parent edit sets its range to the |
| * range covered by its children. If the edit doesn't have |
| * any children its offset is set to the parent's offset |
| * and its length is set to 0. |
| */ |
| public MultiTextEdit() { |
| super(0, Integer.MAX_VALUE); |
| fDefined= false; |
| } |
| |
| /** |
| * Creates a new </code>MultiTextEdit</code> for the given |
| * range. Adding a child to this edit which isn't covered |
| * by the given range will result in an exception. |
| * |
| * @param offset the edit's offset |
| * @param length the edit's length. |
| * @see TextEdit#addChild(TextEdit) |
| * @see TextEdit#addChildren(TextEdit[]) |
| */ |
| public MultiTextEdit(int offset, int length) { |
| super(offset, length); |
| fDefined= true; |
| } |
| |
| /* |
| * Copy constructor. |
| */ |
| protected MultiTextEdit(MultiTextEdit other) { |
| super(other); |
| } |
| |
| /** |
| * Checks the edit's integrity. |
| * <p> |
| * Note that this method <b>should only be called</b> by the edit |
| * framework and not by normal clients.</p> |
| *<p> |
| * This default implementation does nothing. Subclasses may override |
| * if needed.</p> |
| * |
| * @exception MalformedTreeException if the edit isn't in a valid state |
| * and can therefore not be executed |
| */ |
| protected void checkIntegrity() throws MalformedTreeException { |
| // does nothing |
| } |
| |
| @Override |
| final boolean isDefined() { |
| if (fDefined) |
| return true; |
| return hasChildren(); |
| } |
| |
| @Override |
| public final int getOffset() { |
| if (fDefined) |
| return super.getOffset(); |
| |
| List<TextEdit> children= internalGetChildren(); |
| if (children == null || children.size() == 0) |
| return 0; |
| // the children are already sorted |
| return children.get(0).getOffset(); |
| } |
| |
| @Override |
| public final int getLength() { |
| if (fDefined) |
| return super.getLength(); |
| |
| List<TextEdit> children= internalGetChildren(); |
| if (children == null || children.size() == 0) |
| return 0; |
| // the children are already sorted |
| TextEdit first= children.get(0); |
| TextEdit last= children.get(children.size() - 1); |
| return last.getOffset() - first.getOffset() + last.getLength(); |
| } |
| |
| @Override |
| public final boolean covers(TextEdit other) { |
| if (fDefined) |
| return super.covers(other); |
| // an undefined multiple text edit covers everything |
| return true; |
| } |
| |
| @Override |
| protected boolean canZeroLengthCover() { |
| return true; |
| } |
| |
| /* |
| * @see TextEdit#copy |
| */ |
| @Override |
| protected TextEdit doCopy() { |
| Assert.isTrue(MultiTextEdit.class == getClass(), "Subclasses must reimplement copy0"); //$NON-NLS-1$ |
| return new MultiTextEdit(this); |
| } |
| |
| /* |
| * @see TextEdit#accept0 |
| */ |
| @Override |
| protected void accept0(TextEditVisitor visitor) { |
| boolean visitChildren= visitor.visit(this); |
| if (visitChildren) { |
| acceptChildren(visitor); |
| } |
| } |
| |
| @Override |
| void adjustOffset(int delta) { |
| if (fDefined) |
| super.adjustOffset(delta); |
| } |
| |
| @Override |
| void adjustLength(int delta) { |
| if (fDefined) |
| super.adjustLength(delta); |
| } |
| |
| /* |
| * @see TextEdit#performConsistencyCheck |
| */ |
| @Override |
| void performConsistencyCheck(TextEditProcessor processor, IDocument document) throws MalformedTreeException { |
| checkIntegrity(); |
| } |
| |
| /* |
| * @see TextEdit#performDocumentUpdating |
| */ |
| @Override |
| int performDocumentUpdating(IDocument document) throws BadLocationException { |
| fDelta= 0; |
| return fDelta; |
| } |
| |
| /* |
| * @see TextEdit#deleteChildren |
| */ |
| @Override |
| boolean deleteChildren() { |
| return false; |
| } |
| |
| @Override |
| void aboutToBeAdded(TextEdit parent) { |
| defineRegion(parent.getOffset()); |
| } |
| |
| void defineRegion(int parentOffset) { |
| if (fDefined) |
| return; |
| if (hasChildren()) { |
| IRegion region= getCoverage(getChildren()); |
| internalSetOffset(region.getOffset()); |
| internalSetLength(region.getLength()); |
| } else { |
| internalSetOffset(parentOffset); |
| internalSetLength(0); |
| } |
| fDefined= true; |
| } |
| |
| @Override |
| void internalToString(StringBuffer buffer, int indent) { |
| super.internalToString(buffer, indent); |
| if (! fDefined) |
| buffer.append(" [undefined]"); //$NON-NLS-1$ |
| } |
| } |