gt:         return this == obj; // equivalent to Object.isEquals
context: /*******************************************************************************
   * 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.text.BadLocationException;
  import org.eclipse.che.ide.api.text.Region;
  import org.eclipse.che.ide.api.text.RegionImpl;
  import org.eclipse.che.ide.ext.java.jdt.text.Document;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.Iterator;
  import java.util.List;
  
  /**
   * A text edit describes an elementary text manipulation operation. Edits are executed by applying them to a document (e.g. an
   * instance of <code>Document
   * </code>).
   * <p>
   * Text edits form a tree. Clients can navigate the tree upwards, from child to parent, as well as downwards. Newly created edits
   * are un-parented. New edits are added to the tree by calling one of the <code>add</code> methods on a parent edit.
   * </p>
   * <p>
   * An edit tree is well formed in the following sense:
   * <ul>
   * <li>a parent edit covers all its children</li>
   * <li>children don't overlap</li>
   * <li>an edit with length 0 can't have any children</li>
   * </ul>
   * Any manipulation of the tree that violates one of the above requirements results in a <code>MalformedTreeException</code>.
   * </p>
   * <p>
   * Insert edits are represented by an edit of length 0. If more than one insert edit exists at the same offset then the edits are
   * executed in the order in which they have been added to a parent. The following code example:
   * <p/>
   * <pre>
   * Document document = new DocumentImpl(&quot;org&quot;);
   * MultiTextEdit edit = new MultiTextEdit();
   * edit.addChild(new InsertEdit(0, &quot;www.&quot;));
   * edit.addChild(new InsertEdit(0, &quot;eclipse.&quot;));
   * edit.apply(document);
   * </pre>
   * <p/>
   * therefore results in string: "www.eclipse.org".
   * </p>
   * <p>
   * Text edits can be executed in a mode where the edit's region is updated to reflect the edit's position in the changed document.
   * Region updating is enabled by default or can be requested by passing <code>UPDATE_REGIONS</code> to the
   * {@link #apply(Document, int) apply(Document, int)} method. In the above example the region of the
   * <code>InsertEdit(0, "eclipse.")</code> edit after executing the root edit is <code>[3, 8]</code>. If the region of an edit got
   * deleted during change execution the region is set to <code>[-1, -1]</code> and the method {@link #isDeleted() isDeleted}
   * returns <code>true</code>.
   * </p>
   * This class isn't intended to be subclassed outside of the edit framework. Clients are only allowed to subclass
   * <code>MultiTextEdit</code>.
   *
   * @noextend This class is not intended to be subclassed by clients.
   */
  public abstract class TextEdit {
  
      /** Flags indicating that neither <code>CREATE_UNDO</code> nor <code>UPDATE_REGIONS</code> is set. */
      public static final int NONE = 0;
  
      /**
       * Flags indicating that applying an edit tree to a document is supposed to create a corresponding undo edit. If not specified
       * <code>null</code> is returned from method <code>
       * apply</code>.
       */
      public static final int CREATE_UNDO = 1 << 0;
  
      /**
       * Flag indicating that the edit's region will be updated to reflect its position in the changed document. If not specified
       * when applying an edit tree to a document the edit's region will be arbitrary. It is even not guaranteed that the tree is
       * still well formed.
       */
      public static final int UPDATE_REGIONS = 1 << 1;
  
      private static class InsertionComparator implements Comparator<TextEdit> {
          public int compare(TextEdit edit1, TextEdit edit2) throws MalformedTreeException {
              int offset1 = edit1.getOffset();
              int length1 = edit1.getLength();
  
              int offset2 = edit2.getOffset();
              int length2 = edit2.getLength();
  
              if (offset1 == offset2 && length1 == 0 && length2 == 0) {
                  return 0;
              }
              if (offset1 + length1 <= offset2) {
                  return -1;
              }
              if (offset2 + length2 <= offset1) {
                  return 1;
              }
              throw new MalformedTreeException(null, edit1, "Overlapping text edits"); //$NON-NLS-1$
          }
      }
  
      private static final TextEdit[] EMPTY_ARRAY = new TextEdit[0];
  
      private static final InsertionComparator INSERTION_COMPARATOR = new InsertionComparator();
  
      private static final int DELETED_VALUE = -1;
  
      private int fOffset;
  
      private int fLength;
  
      private TextEdit fParent;
  
      private List<TextEdit> fChildren;
  
      int fDelta;
  
      /**
       * Create a new text edit. Parent is initialized to <code>
       * null<code> and the edit doesn't have any children.
       *
       * @param offset
       *         the edit's offset
       * @param length
       *         the edit's length
       */
      protected TextEdit(int offset, int length) {
          // Assert.isTrue(offset >= 0 && length >= 0);
          fOffset = offset;
          fLength = length;
          fDelta = 0;
      }
  
      /**
       * Copy constructor
       *
       * @param source
       *         the source to copy form
       */
      protected TextEdit(TextEdit source) {
          fOffset = source.fOffset;
          fLength = source.fLength;
          fDelta = 0;
      }
  
      // ---- Region management -----------------------------------------------
  
      /**
       * Returns the range that this edit is manipulating. The returned <code>IRegion</code> contains the edit's offset and length at
       * the point in time when this call is made. Any subsequent changes to the edit's offset and length aren't reflected in the
       * returned region object.
       * <p/>
       * Creating a region for a deleted edit will result in an assertion failure.
       *
       * @return the manipulated region
       */
      public final Region getRegion() {
          return new RegionImpl(getOffset(), getLength());
      }
  
      /**
       * Returns the offset of the edit. An offset is a 0-based character index. Returns <code>-1</code> if the edit is marked as
       * deleted.
       *
       * @return the offset of the edit
       */
      public int getOffset() {
          return fOffset;
      }
  
      /**
       * Returns the length of the edit. Returns <code>-1</code> if the edit is marked as deleted.
       *
       * @return the length of the edit
       */
      public int getLength() {
          return fLength;
      }
  
      /**
       * Returns the inclusive end position of this edit. The inclusive end position denotes the last character of the region
       * manipulated by this edit. The returned value is the result of the following calculation:
       * <p/>
       * <pre>
       * getOffset() + getLength() - 1;
       *
       * <pre>
       *
       * @return the inclusive end position
       */
      public final int getInclusiveEnd() {
          return getOffset() + getLength() - 1;
      }
  
      /**
       * Returns the exclusive end position of this edit. The exclusive end position denotes the next character of the region
       * manipulated by this edit. The returned value is the result of the following calculation:
       * <p/>
       * <pre>
       *   getOffset() + getLength();
       * </pre>
       *
       * @return the exclusive end position
       */
      public final int getExclusiveEnd() {
          return getOffset() + getLength();
      }
  
      /**
       * Returns whether this edit has been deleted or not.
       *
       * @return <code>true</code> if the edit has been deleted; otherwise <code>false</code> is returned.
       */
      public final boolean isDeleted() {
          return fOffset == DELETED_VALUE && fLength == DELETED_VALUE;
      }
  
      /**
       * Move all offsets in the tree by the given delta. This node must be a root node. The resulting offsets must be greater or
       * equal to zero.
       *
       * @param delta
       *         the delta
       * @since 3.1
       */
      public final void moveTree(int delta) {
          // Assert.isTrue(fParent == null);
          // Assert.isTrue(getOffset() + delta >= 0);
          internalMoveTree(delta);
      }
  
      /**
       * Returns <code>true</code> if the edit covers the given edit <code>other</code>. It is up to the concrete text edit to decide
       * if a edit of length zero can cover another edit.
       *
       * @param other
       *         the other edit
       * @return <code>true<code> if the edit covers the other edit;
       * otherwise <code>false</code> is returned.
       */
      public boolean covers(TextEdit other) {
          if (getLength() == 0 && !canZeroLengthCover())
              return false;
  
          if (!other.isDefined())
              return true;
  
          int thisOffset = getOffset();
          int otherOffset = other.getOffset();
          return thisOffset <= otherOffset && otherOffset + other.getLength() <= thisOffset + getLength();
      }
  
      /**
       * Returns <code>true</code> if an edit with length zero can cover another edit. Returns <code>false</code> otherwise.
       *
       * @return whether an edit of length zero can cover another edit
       */
      protected boolean canZeroLengthCover() {
          return false;
      }
  
      /**
       * Returns whether the region of this edit is defined or not.
       *
       * @return whether the region of this edit is defined or not
       * @since 3.1
       */
      boolean isDefined() {
          return true;
      }
  
      // ---- parent and children management -----------------------------
  
      /**
       * Returns the edit's parent. The method returns <code>null</code> if this edit hasn't been add to another edit.
       *
       * @return the edit's parent
       */
      public final TextEdit getParent() {
          return fParent;
      }
  
      /**
       * Returns the root edit of the edit tree.
       *
       * @return the root edit of the edit tree
       * @since 3.1
       */
      public final TextEdit getRoot() {
          TextEdit result = this;
          while (result.fParent != null) {
              result = result.fParent;
          }
          return result;
      }
  
      /**
       * Adds the given edit <code>child</code> to this edit.
       *
       * @param child
       *         the child edit to add
       * @throws MalformedTreeException
       *         is thrown if the child edit can't be added to this edit. This is the case if the child
       *         overlaps with one of its siblings or if the child edit's region isn't fully covered by this edit.
       */
      public final void addChild(TextEdit child) throws MalformedTreeException {
          internalAdd(child);
      }
  
      /**
       * Adds all edits in <code>edits</code> to this edit.
       *
       * @param edits
       *         the text edits to add
       * @throws MalformedTreeException
       *         is thrown if one of the given edits can't be added to this edit.
       * @see #addChild(TextEdit)
       */
      public final void addChildren(TextEdit[] edits) throws MalformedTreeException {
          for (int i = 0; i < edits.length; i++) {
              internalAdd(edits[i]);
          }
      }
  
      /**
       * Removes the edit specified by the given index from the list of children. Returns the child edit that was removed from the
       * list of children. The parent of the returned edit is set to <code>null</code>.
       *
       * @param index
       *         the index of the edit to remove
       * @return the removed edit
       * @throws IndexOutOfBoundsException
       *         if the index is out of range
       */
      public final TextEdit removeChild(int index) {
          if (fChildren == null)
              throw new IndexOutOfBoundsException("Index: " + index + " Size: 0"); //$NON-NLS-1$//$NON-NLS-2$
          TextEdit result = (TextEdit)fChildren.remove(index);
          result.internalSetParent(null);
          if (fChildren.isEmpty())
              fChildren = null;
          return result;
      }
  
      /**
       * Removes the first occurrence of the given child from the list of children.
       *
       * @param child
       *         the child to be removed
       * @return <code>true</code> if the edit contained the given child; otherwise <code>false</code> is returned
       */
      public final boolean removeChild(TextEdit child) {
          // Assert.isNotNull(child);
          if (fChildren == null)
              return false;
          boolean result = fChildren.remove(child);
          if (result) {
              child.internalSetParent(null);
              if (fChildren.isEmpty())
                  fChildren = null;
          }
          return result;
      }
  
      /**
       * Removes all child edits from and returns them. The parent of the removed edits is set to <code>null</code>.
       *
       * @return an array of the removed edits
       */
      public final TextEdit[] removeChildren() {
          if (fChildren == null)
              return EMPTY_ARRAY;
          int size = fChildren.size();
          TextEdit[] result = new TextEdit[size];
          for (int i = 0; i < size; i++) {
              result[i] = (TextEdit)fChildren.get(i);
              result[i].internalSetParent(null);
          }
          fChildren = null;
          return result;
      }
  
      /**
       * Returns <code>true</code> if this edit has children. Otherwise <code>false</code> is returned.
       *
       * @return <code>true</code> if this edit has children; otherwise <code>false</code> is returned
       */
      public final boolean hasChildren() {
          return fChildren != null && !fChildren.isEmpty();
      }
  
      /**
       * Returns the edit's children. If the edit doesn't have any children an empty array is returned.
       *
       * @return the edit's children
       */
      public final TextEdit[] getChildren() {
          if (fChildren == null)
              return EMPTY_ARRAY;
          return (TextEdit[])fChildren.toArray(new TextEdit[fChildren.size()]);
      }
  
      /**
       * Returns the size of the managed children.
       *
       * @return the size of the children
       */
      public final int getChildrenSize() {
          if (fChildren == null)
              return 0;
          return fChildren.size();
      }
  
      /**
       * Returns the text range spawned by the given array of text edits. The method requires that the given array contains at least
       * one edit. If all edits passed are deleted the method returns <code>
       * null</code>.
       *
       * @param edits
       *         an array of edits
       * @return the text range spawned by the given array of edits or <code>null</code> if all edits are marked as deleted
       */
      public static Region getCoverage(TextEdit[] edits) {
          // Assert.isTrue(edits != null && edits.length > 0);
  
          int offset = Integer.MAX_VALUE;
          int end = Integer.MIN_VALUE;
          int deleted = 0;
          for (int i = 0; i < edits.length; i++) {
              TextEdit edit = edits[i];
              if (edit.isDeleted()) {
                  deleted++;
              } else {
                  offset = Math.min(offset, edit.getOffset());
                  end = Math.max(end, edit.getExclusiveEnd());
              }
          }
          if (edits.length == deleted)
              return null;
  
          return new RegionImpl(offset, end - offset);
      }
  
      /**
       * Hook called before this edit gets added to the passed parent.
       *
       * @param parent
       *         the parent text edit
       */
      void aboutToBeAdded(TextEdit parent) {
      }
  
      // ---- Object methods ------------------------------------------------------
  
      /**
       * The <code>Edit</code> implementation of this <code>Object</code> method uses object identity (==).
       *
       * @param obj
       *         the other object
       * @return <code>true</code> iff <code>this == obj</code>; otherwise <code>false</code> is returned
       * @see Object#equals(java.lang.Object)
       */
      public final boolean equals(Object obj) {
  