/******************************************************************************* * Copyright (c) 2012-2015 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.text.edits; import org.eclipse.che.ide.api.editor.text.BadLocationException; import org.eclipse.che.ide.ext.java.jdt.text.Document; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A <code>TextEditProcessor</code> manages a set of edits and applies them as a whole to an <code>IDocument</code>. * <p> * This class isn't intended to be subclassed. * </p> * * @noextend This class is not intended to be subclassed by clients. * @see TextEdit#apply(Document) */ public class TextEditProcessor { private Document fDocument; private TextEdit fRoot; private int fStyle; private boolean fChecked; private MalformedTreeException fException; private List fSourceEdits; /** * Constructs a new edit processor for the given document. * * @param document * the document to manipulate * @param root * the root of the text edit tree describing the modifications. By passing a text edit a a text edit processor the * ownership of the edit is transfered to the text edit processors. Clients must not modify the edit (e.g adding new * children) any longer. * @param style * {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or {@link TextEdit#UPDATE_REGIONS}) */ public TextEditProcessor(Document document, TextEdit root, int style) { this(document, root, style, false); } private TextEditProcessor(Document document, TextEdit root, int style, boolean secondary) { // Assert.isNotNull(document); // Assert.isNotNull(root); fDocument = document; fRoot = root; if (fRoot instanceof MultiTextEdit) ((MultiTextEdit)fRoot).defineRegion(0); fStyle = style; if (secondary) { fChecked = true; fSourceEdits = new ArrayList(); } } /** * Creates a special internal processor used to during source computation inside move source and copy source edits * * @param document * the document to be manipulated * @param root * the edit tree * @param style * {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or {@link TextEdit#UPDATE_REGIONS}) * @return a secondary text edit processor * @since 3.1 */ static TextEditProcessor createSourceComputationProcessor(Document document, TextEdit root, int style) { return new TextEditProcessor(document, root, style, true); } /** * Returns the document to be manipulated. * * @return the document */ public Document getDocument() { return fDocument; } /** * Returns the edit processor's root edit. * * @return the processor's root edit */ public TextEdit getRoot() { return fRoot; } /** * Returns the style bits of the text edit processor * * @return the style bits * @see TextEdit#CREATE_UNDO * @see TextEdit#UPDATE_REGIONS */ public int getStyle() { return fStyle; } /** * Checks if the processor can execute all its edits. * * @return <code>true</code> if the edits can be executed. Return <code>false * </code>otherwise. One major reason why edits cannot be executed are wrong offset or length values of edits. Calling perform * in this case will very likely end in a <code>BadLocationException</code>. */ public boolean canPerformEdits() { try { fRoot.dispatchCheckIntegrity(this); fChecked = true; } catch (MalformedTreeException e) { fException = e; return false; } return true; } /** * Executes the text edits. * * @return an object representing the undo of the executed edits * @throws MalformedTreeException * is thrown if the edit tree isn't in a valid state. This exception is thrown before any * edit is executed. So the document is still in its original state. * @throws BadLocationException * is thrown if one of the edits in the tree can't be executed. The state of the document is * undefined if this exception is thrown. */ public UndoEdit performEdits() throws MalformedTreeException, BadLocationException { if (!fChecked) { fRoot.dispatchCheckIntegrity(this); } else { if (fException != null) throw fException; } return fRoot.dispatchPerformEdits(this); } /** * Tells whether this processor considers the given edit. * <p> * Note that this class isn't intended to be subclassed. * </p> * * @param edit * the text edit * @return <code>true</code> if this processor considers the given edit */ protected boolean considerEdit(TextEdit edit) { return true; } // ---- checking -------------------------------------------------------------------- void checkIntegrityDo() throws MalformedTreeException { fSourceEdits = new ArrayList(); fRoot.traverseConsistencyCheck(this, fDocument, fSourceEdits); if (fRoot.getExclusiveEnd() > fDocument.getLength()) throw new MalformedTreeException(null, fRoot, "End position lies outside document range"); //$NON-NLS-1$ } void checkIntegrityUndo() { if (fRoot.getExclusiveEnd() > fDocument.getLength()) throw new MalformedTreeException(null, fRoot, "End position lies outside document range"); //$NON-NLS-1$ } // ---- execution -------------------------------------------------------------------- UndoEdit executeDo() throws BadLocationException { UndoCollector collector = new UndoCollector(fRoot); try { if (createUndo()) collector.connect(fDocument); computeSources(); fRoot.traverseDocumentUpdating(this, fDocument); if (updateRegions()) { fRoot.traverseRegionUpdating(this, fDocument, 0, false); } } finally { collector.disconnect(fDocument); } return collector.undo; } private void computeSources() { for (Iterator iter = fSourceEdits.iterator(); iter.hasNext(); ) { List list = (List)iter.next(); if (list != null) { for (Iterator edits = list.iterator(); edits.hasNext(); ) { TextEdit edit = (TextEdit)edits.next(); edit.traverseSourceComputation(this, fDocument); } } } } UndoEdit executeUndo() throws BadLocationException { UndoCollector collector = new UndoCollector(fRoot); try { if (createUndo()) collector.connect(fDocument); TextEdit[] edits = fRoot.getChildren(); for (int i = edits.length - 1; i >= 0; i--) { edits[i].performDocumentUpdating(fDocument); } } finally { collector.disconnect(fDocument); } return collector.undo; } private boolean createUndo() { return (fStyle & TextEdit.CREATE_UNDO) != 0; } private boolean updateRegions() { return (fStyle & TextEdit.UPDATE_REGIONS) != 0; } }