Package org.eclipse.jface.text

Source Code of org.eclipse.jface.text.AbstractDocument

/*******************************************************************************
* Copyright (c) 2000, 2010 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.jface.text;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.PatternSyntaxException;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;


/**
* Abstract default implementation of <code>IDocument</code> and its extension
* interfaces {@link org.eclipse.jface.text.IDocumentExtension},
* {@link org.eclipse.jface.text.IDocumentExtension2},
* {@link org.eclipse.jface.text.IDocumentExtension3},
* {@link org.eclipse.jface.text.IDocumentExtension4}, as well as
* {@link org.eclipse.jface.text.IRepairableDocument}.
* <p>
*
* An <code>AbstractDocument</code> supports the following implementation
* plug-ins:
* <ul>
* <li>a text store implementing {@link org.eclipse.jface.text.ITextStore} for
*     storing and managing the document's content,</li>
* <li>a line tracker implementing {@link org.eclipse.jface.text.ILineTracker}
*     to map character positions to line numbers and vice versa</li>
* </ul>
* The document can dynamically change the text store when switching between
* sequential rewrite mode and normal mode.
* <p>
*
* This class must be subclassed. Subclasses must configure which implementation
* plug-ins the document instance should use. Subclasses are not intended to
* overwrite existing methods.
*
* @see org.eclipse.jface.text.ITextStore
* @see org.eclipse.jface.text.ILineTracker
*/
public abstract class AbstractDocument implements IDocument, IDocumentExtension, IDocumentExtension2, IDocumentExtension3, IDocumentExtension4, IRepairableDocument, IRepairableDocumentExtension {

  /**
   * Tells whether this class is in debug mode.
   * @since 3.1
   */
  private static final boolean DEBUG= false;


  /**
   * Inner class to bundle a registered post notification replace operation together with its
   * owner.
   *
   * @since 2.0
   */
  static private class RegisteredReplace {
    /** The owner of this replace operation. */
    IDocumentListener fOwner;
    /** The replace operation */
    IDocumentExtension.IReplace fReplace;

    /**
     * Creates a new bundle object.
     * @param owner the document listener owning the replace operation
     * @param replace the replace operation
     */
    RegisteredReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
      fOwner= owner;
      fReplace= replace;
    }
  }


  /** The document's text store */
  private ITextStore   fStore;
  /** The document's line tracker */
  private ILineTracker fTracker;
  /** The registered document listeners */
  private ListenerList fDocumentListeners;
  /** The registered pre-notified document listeners */
  private ListenerList fPrenotifiedDocumentListeners;
  /** The registered document partitioning listeners */
  private ListenerList fDocumentPartitioningListeners;
  /** All positions managed by the document ordered by their start positions. */
  private Map fPositions;
  /**
   * All positions managed by the document ordered by there end positions.
   * @since 3.4
   */
  private Map fEndPositions;
  /** All registered document position updaters */
  private List fPositionUpdaters;
  /**
   * The list of post notification changes
   * @since 2.0
   */
  private List fPostNotificationChanges;
  /**
   * The reentrance count for post notification changes.
   * @since 2.0
   */
  private int fReentranceCount= 0;
  /**
   * Indicates whether post notification change processing has been stopped.
   * @since 2.0
   */
  private int fStoppedCount= 0;
  /**
   * Indicates whether the registration of post notification changes should be ignored.
   * @since 2.1
   */
  private boolean fAcceptPostNotificationReplaces= true;
  /**
   * Indicates whether the notification of listeners has been stopped.
   * @since 2.1
   */
  private int fStoppedListenerNotification= 0;
  /**
   * The document event to be sent after listener notification has been resumed.
   * @since 2.1
   */
  private DocumentEvent fDeferredDocumentEvent;
  /**
   * The registered document partitioners.
   * @since 3.0
   */
  private Map fDocumentPartitioners;
  /**
   * The partitioning changed event.
   * @since 3.0
   */
  private DocumentPartitioningChangedEvent fDocumentPartitioningChangedEvent;
  /**
   * The find/replace document adapter.
   * @since 3.0
   */
  private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
  /**
   * The active document rewrite session.
   * @since 3.1
   */
  private DocumentRewriteSession fDocumentRewriteSession;
  /**
   * The registered document rewrite session listeners.
   * @since 3.1
   */
  private List fDocumentRewriteSessionListeners;
  /**
   * The current modification stamp.
   * @since 3.1
   */
  private long fModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
  /**
   * Keeps track of next modification stamp.
   * @since 3.1.1
   */
  private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
  /**
   * This document's default line delimiter.
   * @since 3.1
   */
  private String fInitialLineDelimiter;


  /**
   * The default constructor does not perform any configuration
   * but leaves it to the clients who must first initialize the
   * implementation plug-ins and then call <code>completeInitialization</code>.
   * Results in the construction of an empty document.
   */
  protected AbstractDocument() {
    fModificationStamp= getNextModificationStamp();
  }


  /**
   * Returns the document's text store. Assumes that the
   * document has been initialized with a text store.
   *
   * @return the document's text store
   */
  protected ITextStore getStore() {
    Assert.isNotNull(fStore);
    return fStore;
  }

  /**
   * Returns the document's line tracker. Assumes that the
   * document has been initialized with a line tracker.
   *
   * @return the document's line tracker
   */
  protected ILineTracker getTracker() {
    Assert.isNotNull(fTracker);
    return fTracker;
  }

  /**
   * Returns the document's document listeners.
   *
   * @return the document's document listeners
   */
  protected List getDocumentListeners() {
    return Arrays.asList(fDocumentListeners.getListeners());
  }

  /**
   * Returns the document's partitioning listeners.
   *
   * @return the document's partitioning listeners
   */
  protected List getDocumentPartitioningListeners() {
    return Arrays.asList(fDocumentPartitioningListeners.getListeners());
  }

  /**
   * Returns all positions managed by the document grouped by category.
   *
   * @return the document's positions
     */
  protected Map getDocumentManagedPositions() {
    return fPositions;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getDocumentPartitioner()
   */
  public IDocumentPartitioner getDocumentPartitioner() {
    return getDocumentPartitioner(DEFAULT_PARTITIONING);
  }



  //--- implementation configuration interface ------------

  /**
   * Sets the document's text store.
   * Must be called at the beginning of the constructor.
   *
   * @param store the document's text store
   */
  protected void setTextStore(ITextStore store) {
    fStore= store;
  }

  /**
   * Sets the document's line tracker.
   * Must be called at the beginning of the constructor.
   *
   * @param tracker the document's line tracker
   */
  protected void setLineTracker(ILineTracker tracker) {
    fTracker= tracker;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#setDocumentPartitioner(org.eclipse.jface.text.IDocumentPartitioner)
   */
  public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
    setDocumentPartitioner(DEFAULT_PARTITIONING, partitioner);
  }

  /**
   * Initializes document listeners, positions, and position updaters.
   * Must be called inside the constructor after the implementation plug-ins
   * have been set.
   */
  protected void completeInitialization() {

    fPositions= new HashMap();
    fEndPositions= new HashMap();
    fPositionUpdaters= new ArrayList();
    fDocumentListeners= new ListenerList(ListenerList.IDENTITY);
    fPrenotifiedDocumentListeners= new ListenerList(ListenerList.IDENTITY);
    fDocumentPartitioningListeners= new ListenerList(ListenerList.IDENTITY);
    fDocumentRewriteSessionListeners= new ArrayList();

    addPositionCategory(DEFAULT_CATEGORY);
    addPositionUpdater(new DefaultPositionUpdater(DEFAULT_CATEGORY));
  }


  //-------------------------------------------------------

  /*
   * @see org.eclipse.jface.text.IDocument#addDocumentListener(org.eclipse.jface.text.IDocumentListener)
   */
  public void addDocumentListener(IDocumentListener listener) {
    Assert.isNotNull(listener);
    fDocumentListeners.add(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removeDocumentListener(org.eclipse.jface.text.IDocumentListener)
   */
  public void removeDocumentListener(IDocumentListener listener) {
    Assert.isNotNull(listener);
    fDocumentListeners.remove(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#addPrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
   */
  public void addPrenotifiedDocumentListener(IDocumentListener listener) {
    Assert.isNotNull(listener);
    fPrenotifiedDocumentListeners.add(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removePrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener)
   */
  public void removePrenotifiedDocumentListener(IDocumentListener listener) {
    Assert.isNotNull(listener);
    fPrenotifiedDocumentListeners.remove(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#addDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
   */
  public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
    Assert.isNotNull(listener);
    fDocumentPartitioningListeners.add(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removeDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener)
   */
  public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
    Assert.isNotNull(listener);
    fDocumentPartitioningListeners.remove(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#addPosition(java.lang.String, org.eclipse.jface.text.Position)
   */
  public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException  {

    if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength()))
      throw new BadLocationException();

    if (category == null)
      throw new BadPositionCategoryException();

    List list= (List) fPositions.get(category);
    if (list == null)
      throw new BadPositionCategoryException();
    list.add(computeIndexInPositionList(list, position.offset), position);

    List endPositions= (List) fEndPositions.get(category);
    if (endPositions == null)
      throw new BadPositionCategoryException();
    endPositions.add(computeIndexInPositionList(endPositions, position.offset + position.length - 1, false), position);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#addPosition(org.eclipse.jface.text.Position)
   */
  public void addPosition(Position position) throws BadLocationException {
    try {
      addPosition(DEFAULT_CATEGORY, position);
    } catch (BadPositionCategoryException e) {
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocument#addPositionCategory(java.lang.String)
   */
  public void addPositionCategory(String category) {

    if (category == null)
      return;

    if (!containsPositionCategory(category)) {
      fPositions.put(category, new ArrayList());
      fEndPositions.put(category, new ArrayList());
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocument#addPositionUpdater(org.eclipse.jface.text.IPositionUpdater)
   */
  public void addPositionUpdater(IPositionUpdater updater) {
    insertPositionUpdater(updater, fPositionUpdaters.size());
  }

  /*
   * @see org.eclipse.jface.text.IDocument#containsPosition(java.lang.String, int, int)
   */
  public boolean containsPosition(String category, int offset, int length) {

    if (category == null)
      return false;

    List list= (List) fPositions.get(category);
    if (list == null)
      return false;

    int size= list.size();
    if (size == 0)
      return false;

    int index= computeIndexInPositionList(list, offset);
    if (index < size) {
      Position p= (Position) list.get(index);
      while (p != null && p.offset == offset) {
        if (p.length == length)
          return true;
        ++ index;
        p= (index < size) ? (Position) list.get(index) : null;
      }
    }

    return false;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#containsPositionCategory(java.lang.String)
   */
  public boolean containsPositionCategory(String category) {
    if (category != null)
      return fPositions.containsKey(category);
    return false;
  }


  /**
   * Computes the index in the list of positions at which a position with the given
   * offset would be inserted. The position is supposed to become the first in this list
   * of all positions with the same offset.
   *
   * @param positions the list in which the index is computed
   * @param offset the offset for which the index is computed
   * @return the computed index
   *
   * @see IDocument#computeIndexInCategory(String, int)
   * @deprecated As of 3.4, replaced by {@link #computeIndexInPositionList(List, int, boolean)}
   */
  protected int computeIndexInPositionList(List positions, int offset) {
    return computeIndexInPositionList(positions, offset, true);
  }

  /**
   * Computes the index in the list of positions at which a position with the given
   * position would be inserted. The position to insert is supposed to become the first
   * in this list of all positions with the same position.
   *
   * @param positions the list in which the index is computed
   * @param offset the offset for which the index is computed
   * @param orderedByOffset <code>true</code> if ordered by offset, false if ordered by end position
   * @return the computed index
   * @since 3.4
   */
  protected int computeIndexInPositionList(List positions, int offset, boolean orderedByOffset) {
    if (positions.size() == 0)
      return 0;

    int left= 0;
    int right= positions.size() -1;
    int mid= 0;
    Position p= null;

    while (left < right) {

      mid= (left + right) / 2;

      p= (Position) positions.get(mid);
      int pOffset= getOffset(orderedByOffset, p);
      if (offset < pOffset) {
        if (left == mid)
          right= left;
        else
          right= mid -1;
      } else if (offset > pOffset) {
        if (right == mid)
          left= right;
        else
          left= mid  +1;
      } else if (offset == pOffset) {
        left= right= mid;
      }

    }

    int pos= left;
    p= (Position) positions.get(pos);
    int pPosition= getOffset(orderedByOffset, p);
    if (offset > pPosition) {
      // append to the end
      pos++;
    } else {
      // entry will become the first of all entries with the same offset
      do {
        --pos;
        if (pos < 0)
          break;
        p= (Position) positions.get(pos);
        pPosition= getOffset(orderedByOffset, p);
      } while (offset == pPosition);
      ++pos;
    }

    Assert.isTrue(0 <= pos && pos <= positions.size());

    return pos;
  }

  /*
   * @since 3.4
   */
  private int getOffset(boolean orderedByOffset, Position position) {
    if (orderedByOffset || position.getLength() == 0)
      return position.getOffset();
    return position.getOffset() + position.getLength() - 1;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#computeIndexInCategory(java.lang.String, int)
   */
  public int computeIndexInCategory(String category, int offset) throws BadLocationException, BadPositionCategoryException {

    if (0 > offset || offset > getLength())
      throw new BadLocationException();

    List c= (List) fPositions.get(category);
    if (c == null)
      throw new BadPositionCategoryException();

    return computeIndexInPositionList(c, offset);
  }

  /**
   * Fires the document partitioning changed notification to all registered
   * document partitioning listeners. Uses a robust iterator.
   *
   * @deprecated as of 2.0. Use <code>fireDocumentPartitioningChanged(IRegion)</code> instead.
   */
  protected void fireDocumentPartitioningChanged() {
    if (fDocumentPartitioningListeners == null)
      return;

    Object[] listeners= fDocumentPartitioningListeners.getListeners();
    for (int i= 0; i < listeners.length; i++)
      ((IDocumentPartitioningListener)listeners[i]).documentPartitioningChanged(this);
  }

  /**
   * Fires the document partitioning changed notification to all registered
   * document partitioning listeners. Uses a robust iterator.
   *
   * @param region the region in which partitioning has changed
   *
   * @see IDocumentPartitioningListenerExtension
   * @since 2.0
   * @deprecated as of 3.0. Use
   *             <code>fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent)</code>
   *             instead.
   */
  protected void fireDocumentPartitioningChanged(IRegion region) {
    if (fDocumentPartitioningListeners == null)
      return;

    Object[] listeners= fDocumentPartitioningListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      IDocumentPartitioningListener l= (IDocumentPartitioningListener)listeners[i];
      try {
        if (l instanceof IDocumentPartitioningListenerExtension)
          ((IDocumentPartitioningListenerExtension)l).documentPartitioningChanged(this, region);
        else
          l.documentPartitioningChanged(this);
      } catch (Exception ex) {
        log(ex);
      }
    }
  }

  /**
   * Fires the document partitioning changed notification to all registered
   * document partitioning listeners. Uses a robust iterator.
   *
   * @param event the document partitioning changed event
   *
   * @see IDocumentPartitioningListenerExtension2
   * @since 3.0
   */
  protected void fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent event) {
    if (fDocumentPartitioningListeners == null)
      return;

    Object[] listeners= fDocumentPartitioningListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      IDocumentPartitioningListener l= (IDocumentPartitioningListener)listeners[i];
      try {
        if (l instanceof IDocumentPartitioningListenerExtension2) {
          IDocumentPartitioningListenerExtension2 extension2= (IDocumentPartitioningListenerExtension2)l;
          extension2.documentPartitioningChanged(event);
        } else if (l instanceof IDocumentPartitioningListenerExtension) {
          IDocumentPartitioningListenerExtension extension= (IDocumentPartitioningListenerExtension)l;
          extension.documentPartitioningChanged(this, event.getCoverage());
        } else {
          l.documentPartitioningChanged(this);
        }
      } catch (Exception ex) {
        log(ex);
      }
    }
  }

  /**
   * Fires the given document event to all registers document listeners informing them
   * about the forthcoming document manipulation. Uses a robust iterator.
   *
   * @param event the event to be sent out
   */
  protected void fireDocumentAboutToBeChanged(DocumentEvent event) {

    // IDocumentExtension
    if (fReentranceCount == 0)
      flushPostNotificationChanges();

    if (fDocumentPartitioners != null) {
      Iterator e= fDocumentPartitioners.values().iterator();
      while (e.hasNext()) {
        IDocumentPartitioner p= (IDocumentPartitioner) e.next();
        if (p instanceof IDocumentPartitionerExtension3) {
          IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) p;
          if (extension.getActiveRewriteSession() != null)
            continue;
        }
        try {
          p.documentAboutToBeChanged(event);
        } catch (Exception ex) {
          log(ex);
        }
      }
    }

    Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      try {
        ((IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
      } catch (Exception ex) {
        log(ex);
      }
    }

    listeners= fDocumentListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      try {
        ((IDocumentListener)listeners[i]).documentAboutToBeChanged(event);
      } catch (Exception ex) {
        log(ex);
      }
    }

  }

  /**
   * Updates document partitioning and document positions according to the
   * specification given by the document event.
   *
   * @param event the document event describing the change to which structures must be adapted
   */
  protected void updateDocumentStructures(DocumentEvent event) {

    if (fDocumentPartitioners != null) {
      fDocumentPartitioningChangedEvent= new DocumentPartitioningChangedEvent(this);
      Iterator e= fDocumentPartitioners.keySet().iterator();
      while (e.hasNext()) {
        String partitioning= (String) e.next();
        IDocumentPartitioner partitioner= (IDocumentPartitioner) fDocumentPartitioners.get(partitioning);

        if (partitioner instanceof IDocumentPartitionerExtension3) {
          IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
          if (extension.getActiveRewriteSession() != null)
            continue;
        }

        if (partitioner instanceof IDocumentPartitionerExtension) {
          IDocumentPartitionerExtension extension= (IDocumentPartitionerExtension) partitioner;
          IRegion r= extension.documentChanged2(event);
          if (r != null)
            fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, r.getOffset(), r.getLength());
        } else {
          if (partitioner.documentChanged(event))
            fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, 0, event.getDocument().getLength());
        }
      }
    }

    if (fPositions.size() > 0)
      updatePositions(event);
  }

  /**
   * Notifies all listeners about the given document change. Uses a robust
   * iterator.
   * <p>
   * Executes all registered post notification replace operation.
   *
   * @param event the event to be sent out.
   */
  protected void doFireDocumentChanged(DocumentEvent event) {
    boolean changed= fDocumentPartitioningChangedEvent != null && !fDocumentPartitioningChangedEvent.isEmpty();
    IRegion change= changed ? fDocumentPartitioningChangedEvent.getCoverage() : null;
    doFireDocumentChanged(event, changed, change);
  }

  /**
   * Notifies all listeners about the given document change.
   * Uses a robust iterator. <p>
   * Executes all registered post notification replace operation.
   *
   * @param event the event to be sent out
   * @param firePartitionChange <code>true</code> if a partition change notification should be sent
   * @param partitionChange the region whose partitioning changed
   * @since 2.0
   * @deprecated as of 3.0. Use <code>doFireDocumentChanged2(DocumentEvent)</code> instead; this method will be removed.
   */
  protected void doFireDocumentChanged(DocumentEvent event, boolean firePartitionChange, IRegion partitionChange) {
    doFireDocumentChanged2(event);
  }

  /**
   * Notifies all listeners about the given document change. Uses a robust
   * iterator.
   * <p>
   * Executes all registered post notification replace operation.
   * <p>
   * This method will be renamed to <code>doFireDocumentChanged</code>.
   *
   * @param event the event to be sent out
   * @since 3.0
   */
  protected void doFireDocumentChanged2(DocumentEvent event) {

    DocumentPartitioningChangedEvent p= fDocumentPartitioningChangedEvent;
    fDocumentPartitioningChangedEvent= null;
    if (p != null && !p.isEmpty())
      fireDocumentPartitioningChanged(p);

    Object[] listeners= fPrenotifiedDocumentListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      try {
        ((IDocumentListener)listeners[i]).documentChanged(event);
      } catch (Exception ex) {
        log(ex);
      }
    }

    listeners= fDocumentListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      try {
        ((IDocumentListener)listeners[i]).documentChanged(event);
      } catch (Exception ex) {
        log(ex);
      }
    }

    // IDocumentExtension
    ++ fReentranceCount;
    try {
      if (fReentranceCount == 1)
        executePostNotificationChanges();
    } finally {
      -- fReentranceCount;
    }
  }

  /**
   * Updates the internal document structures and informs all document listeners
   * if listener notification has been enabled. Otherwise it remembers the event
   * to be sent to the listeners on resume.
   *
   * @param event the document event to be sent out
   */
  protected void fireDocumentChanged(DocumentEvent event) {
    updateDocumentStructures(event);

    if (fStoppedListenerNotification == 0)
      doFireDocumentChanged(event);
    else
      fDeferredDocumentEvent= event;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getChar(int)
   */
  public char getChar(int pos) throws BadLocationException {
    if ((0 > pos) || (pos >= getLength()))
      throw new BadLocationException();
    return getStore().get(pos);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getContentType(int)
   */
  public String getContentType(int offset) throws BadLocationException {
    String contentType= null;
    try {
      contentType= getContentType(DEFAULT_PARTITIONING, offset, false);
      Assert.isNotNull(contentType);
    } catch (BadPartitioningException e) {
      Assert.isTrue(false);
    }
    return contentType;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLegalContentTypes()
   */
  public String[] getLegalContentTypes() {
    String[] contentTypes= null;
    try {
      contentTypes= getLegalContentTypes(DEFAULT_PARTITIONING);
      Assert.isNotNull(contentTypes);
    } catch (BadPartitioningException e) {
      Assert.isTrue(false);
    }
    return contentTypes;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLength()
   */
  public int getLength() {
    return getStore().getLength();
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLineDelimiter(int)
   */
  public String getLineDelimiter(int line) throws BadLocationException {
    return getTracker().getLineDelimiter(line);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLegalLineDelimiters()
   */
  public String[] getLegalLineDelimiters() {
    return getTracker().getLegalLineDelimiters();
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
   * @since 3.1
   */
  public String getDefaultLineDelimiter() {

    String lineDelimiter= null;

    try {
      lineDelimiter= getLineDelimiter(0);
    } catch (BadLocationException x) {
    }

    if (lineDelimiter != null)
      return lineDelimiter;

    if (fInitialLineDelimiter != null)
      return fInitialLineDelimiter;

    String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
    String[] delimiters= getLegalLineDelimiters();
    Assert.isTrue(delimiters.length > 0);
    for (int i= 0; i < delimiters.length; i++) {
      if (delimiters[i].equals(sysLineDelimiter)) {
        lineDelimiter= sysLineDelimiter;
        break;
      }
    }

    if (lineDelimiter == null)
      lineDelimiter= delimiters[0];

    return lineDelimiter;

  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String)
   * @since 3.1
   */
  public void setInitialLineDelimiter(String lineDelimiter) {
    Assert.isNotNull(lineDelimiter);
    fInitialLineDelimiter= lineDelimiter;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLineLength(int)
   */
  public int getLineLength(int line) throws BadLocationException {
    return getTracker().getLineLength(line);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLineOfOffset(int)
   */
  public int getLineOfOffset(int pos) throws BadLocationException {
    return getTracker().getLineNumberOfOffset(pos);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLineOffset(int)
   */
  public int getLineOffset(int line) throws BadLocationException {
    return getTracker().getLineOffset(line);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLineInformation(int)
   */
  public IRegion getLineInformation(int line) throws BadLocationException {
    return getTracker().getLineInformation(line);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getLineInformationOfOffset(int)
   */
  public IRegion getLineInformationOfOffset(int offset) throws BadLocationException {
    return getTracker().getLineInformationOfOffset(offset);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getNumberOfLines()
   */
  public int getNumberOfLines() {
    return getTracker().getNumberOfLines();
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getNumberOfLines(int, int)
   */
  public int getNumberOfLines(int offset, int length) throws BadLocationException {
    return getTracker().getNumberOfLines(offset, length);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#computeNumberOfLines(java.lang.String)
   */
  public int computeNumberOfLines(String text) {
    return getTracker().computeNumberOfLines(text);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getPartition(int)
   */
  public ITypedRegion getPartition(int offset) throws BadLocationException {
    ITypedRegion partition= null;
    try {
      partition= getPartition(DEFAULT_PARTITIONING, offset, false);
      Assert.isNotNull(partition);
    } catch (BadPartitioningException e) {
      Assert.isTrue(false);
    }
    return  partition;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#computePartitioning(int, int)
   */
  public ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException {
    ITypedRegion[] partitioning= null;
    try {
      partitioning= computePartitioning(DEFAULT_PARTITIONING, offset, length, false);
      Assert.isNotNull(partitioning);
    } catch (BadPartitioningException e) {
      Assert.isTrue(false);
    }
    return partitioning;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getPositions(java.lang.String)
   */
  public Position[] getPositions(String category) throws BadPositionCategoryException {

    if (category == null)
      throw new BadPositionCategoryException();

    List c= (List) fPositions.get(category);
    if (c == null)
      throw new BadPositionCategoryException();

    Position[] positions= new Position[c.size()];
    c.toArray(positions);
    return positions;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getPositionCategories()
   */
  public String[] getPositionCategories() {
    String[] categories= new String[fPositions.size()];
    Iterator keys= fPositions.keySet().iterator();
    for (int i= 0; i < categories.length; i++)
      categories[i]= (String) keys.next();
    return categories;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#getPositionUpdaters()
   */
  public IPositionUpdater[] getPositionUpdaters() {
    IPositionUpdater[] updaters= new IPositionUpdater[fPositionUpdaters.size()];
    fPositionUpdaters.toArray(updaters);
    return updaters;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#get()
   */
  public String get() {
    return getStore().get(0, getLength());
  }

  /*
   * @see org.eclipse.jface.text.IDocument#get(int, int)
   */
  public String get(int pos, int length) throws BadLocationException {
    int myLength= getLength();
    if ((0 > pos) || (0 > length) || (pos + length > myLength))
      throw new BadLocationException();
    return getStore().get(pos, length);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#insertPositionUpdater(org.eclipse.jface.text.IPositionUpdater, int)
   */
  public void insertPositionUpdater(IPositionUpdater updater, int index) {

    for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
      if (fPositionUpdaters.get(i) == updater)
        return;
    }

    if (index == fPositionUpdaters.size())
      fPositionUpdaters.add(updater);
    else
      fPositionUpdaters.add(index, updater);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, org.eclipse.jface.text.Position)
   */
  public void removePosition(String category, Position position) throws BadPositionCategoryException {

    if (position == null)
      return;

    if (category == null)
      throw new BadPositionCategoryException();

    List c= (List) fPositions.get(category);
    if (c == null)
      throw new BadPositionCategoryException();
    removeFromPositionsList(c, position, true);

    List endPositions= (List) fEndPositions.get(category);
    if (endPositions == null)
      throw new BadPositionCategoryException();
    removeFromPositionsList(endPositions, position, false);
  }

  /**
   * Remove the given position form the given list of positions based on identity not equality.
   *
   * @param positions a list of positions
   * @param position the position to remove
   * @param orderedByOffset true if <code>positions</code> is ordered by offset, false if ordered by end position
   * @since 3.4
   */
  private void removeFromPositionsList(List positions, Position position, boolean orderedByOffset) {
    int size= positions.size();

    //Assume position is somewhere near it was before
    int index= computeIndexInPositionList(positions, orderedByOffset ? position.offset : position.offset + position.length - 1, orderedByOffset);
    if (index < size && positions.get(index) == position) {
      positions.remove(index);
      return;
    }

    int back= index - 1;
    int forth= index + 1;
    while (back >= 0 || forth < size) {
      if (back >= 0) {
        if (position == positions.get(back)) {
          positions.remove(back);
          return;
        }
        back--;
      }

      if (forth < size) {
        if (position == positions.get(forth)) {
          positions.remove(forth);
          return;
        }
        forth++;
      }
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removePosition(org.eclipse.jface.text.Position)
   */
  public void removePosition(Position position) {
    try {
      removePosition(DEFAULT_CATEGORY, position);
    } catch (BadPositionCategoryException e) {
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removePositionCategory(java.lang.String)
   */
  public void removePositionCategory(String category) throws BadPositionCategoryException {

    if (category == null)
      return;

    if ( !containsPositionCategory(category))
      throw new BadPositionCategoryException();

    fPositions.remove(category);
    fEndPositions.remove(category);
  }

  /*
   * @see org.eclipse.jface.text.IDocument#removePositionUpdater(org.eclipse.jface.text.IPositionUpdater)
   */
  public void removePositionUpdater(IPositionUpdater updater) {
    for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
      if (fPositionUpdaters.get(i) == updater) {
        fPositionUpdaters.remove(i);
        return;
      }
    }
  }

  private long getNextModificationStamp() {
    if (fNextModificationStamp == Long.MAX_VALUE || fNextModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP)
      fNextModificationStamp= 0;
    else
      fNextModificationStamp= fNextModificationStamp + 1;

    return fNextModificationStamp;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp()
   * @since 3.1
   */
  public long getModificationStamp() {
    return fModificationStamp;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
   * @since 3.1
   */
  public void replace(int pos, int length, String text, long modificationStamp) throws BadLocationException {
    if ((0 > pos) || (0 > length) || (pos + length > getLength()))
      throw new BadLocationException();

    DocumentEvent e= new DocumentEvent(this, pos, length, text);
    fireDocumentAboutToBeChanged(e);

    getStore().replace(pos, length, text);
    getTracker().replace(pos, length, text);

    fModificationStamp= modificationStamp;
    fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
    e.fModificationStamp= fModificationStamp;

    fireDocumentChanged(e);
  }

  /**
   * {@inheritDoc}
   *
   * @since 3.4
   */
  public boolean isLineInformationRepairNeeded(int offset, int length, String text) throws BadLocationException {
    return false;
  }

  /*
   * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
   */
  public void replace(int pos, int length, String text) throws BadLocationException {
    if (length == 0 && (text == null || text.length() == 0))
      replace(pos, length, text, getModificationStamp());
    else
      replace(pos, length, text, getNextModificationStamp());
  }

  /*
   * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
   */
  public void set(String text) {
    set(text, getNextModificationStamp());
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#set(java.lang.String, long)
   * @since 3.1
   */
  public void set(String text, long modificationStamp) {
    int length= getStore().getLength();

    DocumentEvent e= new DocumentEvent(this, 0, length, text);
    fireDocumentAboutToBeChanged(e);

    getStore().set(text);
    getTracker().set(text);

    fModificationStamp= modificationStamp;
    fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp);
    e.fModificationStamp= fModificationStamp;

    fireDocumentChanged(e);
  }

  /**
   * Updates all positions of all categories to the change described by the
   * document event. All registered document updaters are called in the
   * sequence they have been arranged. Uses a robust iterator.
   *
   * @param event the document event describing the change to which to adapt
   *            the positions
   */
  protected void updatePositions(DocumentEvent event) {
    List list= new ArrayList(fPositionUpdaters);
    Iterator e= list.iterator();
    while (e.hasNext()) {
      IPositionUpdater u= (IPositionUpdater) e.next();
      u.update(event);
    }
  }

  /**
   * {@inheritDoc}
   *
   * @deprecated as of 3.0 search is provided by {@link FindReplaceDocumentAdapter}
   */
  public int search(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException {
    try {
      IRegion region= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
      return region == null ?  -1 : region.getOffset();
    } catch (IllegalStateException ex) {
      return -1;
    } catch (PatternSyntaxException ex) {
      return -1;
    }
  }

  /**
   * Returns the find/replace adapter for this document.
   *
   * @return this document's find/replace document adapter
   * @since 3.0
   */
  private FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
    if (fFindReplaceDocumentAdapter == null)
      fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(this);

    return fFindReplaceDocumentAdapter;
  }

  /**
   * Flushes all registered post notification changes.
   *
   * @since 2.0
   */
  private void flushPostNotificationChanges() {
    if (fPostNotificationChanges != null)
      fPostNotificationChanges.clear();
  }

  /**
   * Executes all registered post notification changes. The process is
   * repeated until no new post notification changes are added.
   *
   * @since 2.0
   */
  private void executePostNotificationChanges() {

    if (fStoppedCount > 0)
      return;

    while (fPostNotificationChanges != null) {
      List changes= fPostNotificationChanges;
      fPostNotificationChanges= null;

      Iterator e= changes.iterator();
      while (e.hasNext()) {
        RegisteredReplace replace= (RegisteredReplace) e.next();
        replace.fReplace.perform(this, replace.fOwner);
      }
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension2#acceptPostNotificationReplaces()
   * @since 2.1
   */
  public void acceptPostNotificationReplaces() {
    fAcceptPostNotificationReplaces= true;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension2#ignorePostNotificationReplaces()
   * @since 2.1
   */
  public void ignorePostNotificationReplaces() {
    fAcceptPostNotificationReplaces= false;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener, org.eclipse.jface.text.IDocumentExtension.IReplace)
   * @since 2.0
   */
  public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
    if (fAcceptPostNotificationReplaces) {
      if (fPostNotificationChanges == null)
        fPostNotificationChanges= new ArrayList(1);
      fPostNotificationChanges.add(new RegisteredReplace(owner, replace));
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing()
   * @since 2.0
   */
  public void stopPostNotificationProcessing() {
    ++ fStoppedCount;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing()
   * @since 2.0
   */
  public void resumePostNotificationProcessing() {
    -- fStoppedCount;
    if (fStoppedCount == 0 && fReentranceCount == 0)
      executePostNotificationChanges();
  }

  /**
   * {@inheritDoc}
   *
   * @since 2.0
   * @deprecated since 3.1. Use
   *             {@link IDocumentExtension4#startRewriteSession(DocumentRewriteSessionType)}
   *             instead.
   */
  public void startSequentialRewrite(boolean normalized) {
  }

  /**
   * {@inheritDoc}
   *
   * @since 2.0
   * @deprecated As of 3.1, replaced by {@link IDocumentExtension4#stopRewriteSession(DocumentRewriteSession)}
   */
  public void stopSequentialRewrite() {
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension2#resumeListenerNotification()
   * @since 2.1
   */
  public void resumeListenerNotification() {
    -- fStoppedListenerNotification;
    if (fStoppedListenerNotification == 0) {
      resumeDocumentListenerNotification();
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension2#stopListenerNotification()
   * @since 2.1
   */
  public void stopListenerNotification() {
    ++ fStoppedListenerNotification;
  }

  /**
   * Resumes the document listener notification by sending out the remembered
   * partition changed and document event.
   *
   * @since 2.1
   */
  private void resumeDocumentListenerNotification() {
    if (fDeferredDocumentEvent != null) {
      DocumentEvent event= fDeferredDocumentEvent;
      fDeferredDocumentEvent= null;
      doFireDocumentChanged(event);
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#computeZeroLengthPartitioning(java.lang.String, int, int)
   * @since 3.0
   */
  public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, boolean includeZeroLengthPartitions) throws BadLocationException, BadPartitioningException {
    if ((0 > offset) || (0 > length) || (offset + length > getLength()))
      throw new BadLocationException();

    IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);

    if (partitioner instanceof IDocumentPartitionerExtension2) {
      checkStateOfPartitioner(partitioner, partitioning);
      return ((IDocumentPartitionerExtension2) partitioner).computePartitioning(offset, length, includeZeroLengthPartitions);
    } else if (partitioner != null) {
      checkStateOfPartitioner(partitioner, partitioning);
      return partitioner.computePartitioning(offset, length);
    } else if (DEFAULT_PARTITIONING.equals(partitioning))
      return new TypedRegion[] { new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE) };
    else
      throw new BadPartitioningException();
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthContentType(java.lang.String, int)
   * @since 3.0
   */
  public String getContentType(String partitioning, int offset, boolean preferOpenPartitions) throws BadLocationException, BadPartitioningException {
    if ((0 > offset) || (offset > getLength()))
      throw new BadLocationException();

    IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);

    if (partitioner instanceof IDocumentPartitionerExtension2) {
      checkStateOfPartitioner(partitioner, partitioning);
      return ((IDocumentPartitionerExtension2) partitioner).getContentType(offset, preferOpenPartitions);
    } else if (partitioner != null) {
      checkStateOfPartitioner(partitioner, partitioning);
      return partitioner.getContentType(offset);
    } else if (DEFAULT_PARTITIONING.equals(partitioning))
      return DEFAULT_CONTENT_TYPE;
    else
      throw new BadPartitioningException();
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#getDocumentPartitioner(java.lang.String)
   * @since 3.0
   */
  public IDocumentPartitioner getDocumentPartitioner(String partitioning)  {
    return fDocumentPartitioners != null ? (IDocumentPartitioner) fDocumentPartitioners.get(partitioning) : null;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#getLegalContentTypes(java.lang.String)
   * @since 3.0
   */
  public String[] getLegalContentTypes(String partitioning) throws BadPartitioningException {
    IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);
    if (partitioner != null)
      return partitioner.getLegalContentTypes();
    if (DEFAULT_PARTITIONING.equals(partitioning))
      return new String[] { DEFAULT_CONTENT_TYPE };
    throw new BadPartitioningException();
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#getZeroLengthPartition(java.lang.String, int)
   * @since 3.0
   */
  public ITypedRegion getPartition(String partitioning, int offset, boolean preferOpenPartitions) throws BadLocationException, BadPartitioningException {
    if ((0 > offset) || (offset > getLength()))
      throw new BadLocationException();

    IDocumentPartitioner partitioner= getDocumentPartitioner(partitioning);

    if (partitioner instanceof IDocumentPartitionerExtension2) {
      checkStateOfPartitioner(partitioner, partitioning);
      return ((IDocumentPartitionerExtension2) partitioner).getPartition(offset, preferOpenPartitions);
    } else if (partitioner != null) {
      checkStateOfPartitioner(partitioner, partitioning);
      return partitioner.getPartition(offset);
    } else if (DEFAULT_PARTITIONING.equals(partitioning))
      return new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
    else
      throw new BadPartitioningException();
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#getPartitionings()
   * @since 3.0
   */
  public String[] getPartitionings() {
    if (fDocumentPartitioners == null)
      return new String[0];
    String[] partitionings= new String[fDocumentPartitioners.size()];
    fDocumentPartitioners.keySet().toArray(partitionings);
    return partitionings;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension3#setDocumentPartitioner(java.lang.String, org.eclipse.jface.text.IDocumentPartitioner)
   * @since 3.0
   */
  public void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner) {
    if (partitioner == null) {
      if (fDocumentPartitioners != null) {
        fDocumentPartitioners.remove(partitioning);
        if (fDocumentPartitioners.size() == 0)
          fDocumentPartitioners= null;
      }
    } else {
      if (fDocumentPartitioners == null)
        fDocumentPartitioners= new HashMap();
      fDocumentPartitioners.put(partitioning, partitioner);
    }
    DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
    event.setPartitionChange(partitioning, 0, getLength());
    fireDocumentPartitioningChanged(event);
  }

  /*
   * @see org.eclipse.jface.text.IRepairableDocument#repairLineInformation()
   * @since 3.0
   */
  public void repairLineInformation() {
    getTracker().set(get());
  }

  /**
   * Fires the given event to all registered rewrite session listeners. Uses robust iterators.
   *
   * @param event the event to be fired
   * @since 3.1
   */
  protected void fireRewriteSessionChanged(DocumentRewriteSessionEvent event) {
    if (fDocumentRewriteSessionListeners.size() > 0) {
      List list= new ArrayList(fDocumentRewriteSessionListeners);
      Iterator e= list.iterator();
      while (e.hasNext()) {
        try {
          IDocumentRewriteSessionListener l= (IDocumentRewriteSessionListener)e.next();
          l.documentRewriteSessionChanged(event);
        } catch (Exception ex) {
          log(ex);
        }
      }
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#getActiveRewriteSession()
   */
  public final DocumentRewriteSession getActiveRewriteSession() {
    return fDocumentRewriteSession;
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSessionType)
   * @since 3.1
   */
  public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) {

    if (getActiveRewriteSession() != null)
      throw new IllegalStateException();


    fDocumentRewriteSession= new DocumentRewriteSession(sessionType);
    if (DEBUG)
      System.out.println("AbstractDocument: Starting rewrite session: " + fDocumentRewriteSession); //$NON-NLS-1$

    fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, fDocumentRewriteSession, DocumentRewriteSessionEvent.SESSION_START));

    startRewriteSessionOnPartitioners(fDocumentRewriteSession);

    ILineTracker tracker= getTracker();
    if (tracker instanceof ILineTrackerExtension) {
      ILineTrackerExtension extension= (ILineTrackerExtension) tracker;
      extension.startRewriteSession(fDocumentRewriteSession);
    }

    if (DocumentRewriteSessionType.SEQUENTIAL == sessionType)
      startSequentialRewrite(false);
    else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType)
      startSequentialRewrite(true);

    return fDocumentRewriteSession;
  }

  /**
   * Starts the given rewrite session.
   *
   * @param session the rewrite session
   * @since 3.1
   */
  protected final void startRewriteSessionOnPartitioners(DocumentRewriteSession session) {
    if (fDocumentPartitioners != null) {
      Iterator e= fDocumentPartitioners.values().iterator();
      while (e.hasNext()) {
        Object partitioner= e.next();
        if (partitioner instanceof IDocumentPartitionerExtension3) {
          IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
          extension.startRewriteSession(session);
        }
      }
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#stopRewriteSession(org.eclipse.jface.text.DocumentRewriteSession)
   * @since 3.1
   */
  public void stopRewriteSession(DocumentRewriteSession session) {
    if (fDocumentRewriteSession == session) {

      if (DEBUG)
        System.out.println("AbstractDocument: Stopping rewrite session: " + session); //$NON-NLS-1$

      DocumentRewriteSessionType sessionType= session.getSessionType();
      if (DocumentRewriteSessionType.SEQUENTIAL == sessionType || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType)
        stopSequentialRewrite();

      ILineTracker tracker= getTracker();
      if (tracker instanceof ILineTrackerExtension) {
        ILineTrackerExtension extension= (ILineTrackerExtension) tracker;
        extension.stopRewriteSession(session, get());
      }

      stopRewriteSessionOnPartitioners(fDocumentRewriteSession);

      fDocumentRewriteSession= null;
      fireRewriteSessionChanged(new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_STOP));
    }
  }

  /**
   * Stops the given rewrite session.
   *
   * @param session the rewrite session
   * @since 3.1
   */
  protected final void stopRewriteSessionOnPartitioners(DocumentRewriteSession session) {
    if (fDocumentPartitioners != null) {
      DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
      Iterator e= fDocumentPartitioners.keySet().iterator();
      while (e.hasNext()) {
        String partitioning= (String) e.next();
        IDocumentPartitioner partitioner= (IDocumentPartitioner) fDocumentPartitioners.get(partitioning);
        if (partitioner instanceof IDocumentPartitionerExtension3) {
          IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
          extension.stopRewriteSession(session);
          event.setPartitionChange(partitioning, 0, getLength());
        }
      }
      if (!event.isEmpty())
        fireDocumentPartitioningChanged(event);
    }
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
   * @since 3.1
   */
  public void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
    Assert.isNotNull(listener);
    if (! fDocumentRewriteSessionListeners.contains(listener))
      fDocumentRewriteSessionListeners.add(listener);
  }

  /*
   * @see org.eclipse.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener)
   * @since 3.1
   */
  public void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) {
    Assert.isNotNull(listener);
    fDocumentRewriteSessionListeners.remove(listener);
  }

  /**
   * Checks the state for the given partitioner and stops the
   * active rewrite session.
   *
   * @param partitioner the document partitioner to be checked
   * @param partitioning the document partitioning the partitioner is registered for
   * @since 3.1
   */
  protected final void checkStateOfPartitioner(IDocumentPartitioner partitioner, String partitioning) {
    if (!(partitioner instanceof IDocumentPartitionerExtension3))
      return;

    IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner;
    DocumentRewriteSession session= extension.getActiveRewriteSession();
    if (session != null) {
      extension.stopRewriteSession(session);

      if (DEBUG)
        System.out.println("AbstractDocument: Flushing rewrite session for partition type: " + partitioning); //$NON-NLS-1$

      DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this);
      event.setPartitionChange(partitioning, 0, getLength());
      fireDocumentPartitioningChanged(event);
    }
  }

  /**
   * Returns all positions of the given category that are inside the given region.
   *
   * @param category the position category
   * @param offset the start position of the region, must be >= 0
   * @param length the length of the region, must be >= 0
   * @param canStartBefore if <code>true</code> then positions are included
   *            which start before the region if they end at or after the regions start
   * @param canEndAfter if <code>true</code> then positions are included
   *            which end after the region if they start at or before the regions end
   * @return all positions inside the region of the given category
   * @throws BadPositionCategoryException if category is undefined in this document
   * @since 3.4
   */
  public Position[] getPositions(String category, int offset, int length, boolean canStartBefore, boolean canEndAfter) throws BadPositionCategoryException {
    if (canStartBefore && canEndAfter || (!canStartBefore && !canEndAfter)) {
      List documentPositions;
      if (canStartBefore && canEndAfter) {
        if (offset < getLength() / 2) {
          documentPositions= getStartingPositions(category, 0, offset + length);
        } else {
          documentPositions= getEndingPositions(category, offset, getLength() - offset + 1);
        }
      } else {
        documentPositions= getStartingPositions(category, offset, length);
      }

      ArrayList list= new ArrayList(documentPositions.size());

      Position region= new Position(offset, length);

      for (Iterator iterator= documentPositions.iterator(); iterator.hasNext();) {
        Position position= (Position) iterator.next();
        if (isWithinRegion(region, position, canStartBefore, canEndAfter)) {
          list.add(position);
        }
      }

      Position[] positions= new Position[list.size()];
      list.toArray(positions);
      return positions;
    } else if (canStartBefore) {
      List list= getEndingPositions(category, offset, length);
      Position[] positions= new Position[list.size()];
      list.toArray(positions);
      return positions;
    } else {
      Assert.isLegal(canEndAfter && !canStartBefore);

      List list= getStartingPositions(category, offset, length);
      Position[] positions= new Position[list.size()];
      list.toArray(positions);
      return positions;
    }
  }

  /*
   * @since 3.4
   */
  private boolean isWithinRegion(Position region, Position position, boolean canStartBefore, boolean canEndAfter) {
    if (canStartBefore && canEndAfter) {
      return region.overlapsWith(position.getOffset(), position.getLength());
    } else if (canStartBefore) {
      return region.includes(position.getOffset() + position.getLength() - 1);
    } else if (canEndAfter) {
      return region.includes(position.getOffset());
    } else {
      int start= position.getOffset();
      return region.includes(start) && region.includes(start + position.getLength() - 1);
    }
  }

  /**
   * A list of positions in the given category with an offset inside the given
   * region. The order of the positions is arbitrary.
   *
   * @param category the position category
   * @param offset the offset of the region
   * @param length the length of the region
   * @return a list of the positions in the region
   * @throws BadPositionCategoryException if category is undefined in this document
   * @since 3.4
   */
  private List getStartingPositions(String category, int offset, int length) throws BadPositionCategoryException {
    List positions= (List) fPositions.get(category);
    if (positions == null)
      throw new BadPositionCategoryException();

    int indexStart= computeIndexInPositionList(positions, offset, true);
    int indexEnd= computeIndexInPositionList(positions, offset + length, true);

    return positions.subList(indexStart, indexEnd);
  }

  /**
   * A list of positions in the given category with an end position inside
   * the given region. The order of the positions is arbitrary.
   *
   * @param category the position category
   * @param offset the offset of the region
   * @param length the length of the region
   * @return a list of the positions in the region
   * @throws BadPositionCategoryException if category is undefined in this document
   * @since 3.4
   */
  private List getEndingPositions(String category, int offset, int length) throws BadPositionCategoryException {
    List positions= (List) fEndPositions.get(category);
    if (positions == null)
      throw new BadPositionCategoryException();

    int indexStart= computeIndexInPositionList(positions, offset, false);
    int indexEnd= computeIndexInPositionList(positions, offset + length, false);

    return positions.subList(indexStart, indexEnd);
  }

  /**
   * Logs the given exception by reusing the code in {@link SafeRunner}.
   *
   * @param ex the exception
   * @since 3.6
   */
  private static void log(final Exception ex) {
    SafeRunner.run(new ISafeRunnable() {
      public void run() throws Exception {
        throw ex;
      }

      public void handleException(Throwable exception) {
        // NOTE: Logging is done by SafeRunner
      }
    });
  }

}
TOP

Related Classes of org.eclipse.jface.text.AbstractDocument

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.