/******************************************************************************* * Copyright (c) 2007, 2012 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 * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.changegenerator; import org.eclipse.text.edits.CopyTargetEdit; import org.eclipse.text.edits.DeleteEdit; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MoveSourceEdit; import org.eclipse.text.edits.MoveTargetEdit; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; public class TextEditUtil { // Do not instantiate. All methods are static. private TextEditUtil() { } /** * Degenerates the given edit tree into a list.<br> * All nodes of the result are leafs.<br> * <strong>The given edit is modified and can no longer be used.</strong> * * @param edit the edit tree to flatten * @return a MultiTextEdit containing the list of edits */ public static MultiTextEdit flatten(TextEdit edit) { MultiTextEdit result= new MultiTextEdit(); flatten(edit, result); return result; } private static void flatten(TextEdit edit, MultiTextEdit result) { if (edit.hasChildren()) { TextEdit[] children= edit.getChildren(); for (int i= 0; i < children.length; i++) { TextEdit child= children[i]; child.getParent().removeChild(0); flatten(child, result); } } else if (!(edit instanceof MultiTextEdit)) { result.addChild(edit); } } /** * Create an edit which contains <code>edit1</code> and <code>edit2</code> * <p><strong>The given edits are modified and they can no longer be used.</strong></p> * * @param edit1 the edit to merge with edit2 * @param edit2 the edit to merge with edit1 * @return the merged tree * @throws MalformedTreeException if the two edits overlap */ public static TextEdit merge(TextEdit edit1, TextEdit edit2) { if (edit1 instanceof MultiTextEdit && !edit1.hasChildren()) { return edit2; } if (edit2 instanceof MultiTextEdit && !edit2.hasChildren()) { return edit1; } MultiTextEdit result= new MultiTextEdit(); merge(edit1, edit2, result); return result; } private static void merge(TextEdit edit1, TextEdit edit2, MultiTextEdit result) { if (edit1 instanceof MultiTextEdit && edit2 instanceof MultiTextEdit) { MultiTextEdit multiTextEdit1= (MultiTextEdit) edit1; if (!multiTextEdit1.hasChildren()) { result.addChild(edit2); return; } MultiTextEdit multiTextEdit2= (MultiTextEdit) edit2; if (!multiTextEdit2.hasChildren()) { result.addChild(edit1); return; } TextEdit[] children1= multiTextEdit1.getChildren(); TextEdit[] children2= multiTextEdit2.getChildren(); int i1= 0; int i2= 0; while (i1 < children1.length && i2 < children2.length) { while (i1 < children1.length && children1[i1].getExclusiveEnd() < children2[i2].getOffset()) { edit1.removeChild(0); result.addChild(children1[i1]); i1++; } if (i1 >= children1.length) break; while (i2 < children2.length && children2[i2].getExclusiveEnd() < children1[i1].getOffset()) { edit2.removeChild(0); result.addChild(children2[i2]); i2++; } if (i2 >= children2.length) break; if (children1[i1].getExclusiveEnd() < children2[i2].getOffset()) continue; edit1.removeChild(0); edit2.removeChild(0); merge(children1[i1], children2[i2], result); i1++; i2++; } while (i1 < children1.length) { edit1.removeChild(0); result.addChild(children1[i1]); i1++; } while (i2 < children2.length) { edit2.removeChild(0); result.addChild(children2[i2]); i2++; } } else if (edit1 instanceof MultiTextEdit) { TextEdit[] children= edit1.getChildren(); int i= 0; while (children[i].getExclusiveEnd() < edit2.getOffset()) { edit1.removeChild(0); result.addChild(children[i]); i++; if (i >= children.length) { result.addChild(edit2); return; } } edit1.removeChild(0); merge(children[i], edit2, result); i++; while (i < children.length) { edit1.removeChild(0); result.addChild(children[i]); i++; } } else if (edit2 instanceof MultiTextEdit) { TextEdit[] children= edit2.getChildren(); int i= 0; while (children[i].getExclusiveEnd() < edit1.getOffset()) { edit2.removeChild(0); result.addChild(children[i]); i++; if (i >= children.length) { result.addChild(edit1); return; } } edit2.removeChild(0); merge(edit1, children[i], result); i++; while (i < children.length) { edit2.removeChild(0); result.addChild(children[i]); i++; } } else { if (edit1.getExclusiveEnd() < edit2.getOffset()) { result.addChild(edit1); result.addChild(edit2); } else { result.addChild(edit2); result.addChild(edit1); } } } /** * Returns the difference in the document length caused by the edit. {@code InsertEdit}s have * positive delta, {@code DeleteEdit}s have negative one. * @param edit the edit to determine delta for. * @return the delta */ public static int delta(TextEdit edit) { int delta = 0; for (TextEdit child : edit.getChildren()) { delta += delta(child); } delta += ownDelta(edit); return delta; } private static int ownDelta(TextEdit edit) { if (edit instanceof DeleteEdit || edit instanceof MoveSourceEdit) { return -edit.getLength(); } else if (edit instanceof InsertEdit) { return ((InsertEdit) edit).getText().length(); } else if (edit instanceof ReplaceEdit) { return ((ReplaceEdit) edit).getText().length() - edit.getLength(); } else if (edit instanceof CopyTargetEdit) { return ((CopyTargetEdit) edit).getSourceEdit().getLength(); } else if (edit instanceof MoveTargetEdit) { return ((MoveTargetEdit) edit).getSourceEdit().getLength(); } return 0; } }