2  * (c) Copyright IBM Corp. 2000, 2001.
 
   5 package net.sourceforge.phpdt.internal.corext.textmanipulation;
 
   7 import java.util.ArrayList;
 
  10 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
  11 import org.eclipse.core.resources.IFile;
 
  12 import org.eclipse.core.runtime.CoreException;
 
  13 import org.eclipse.core.runtime.IProgressMonitor;
 
  14 import org.eclipse.core.runtime.IStatus;
 
  15 import org.eclipse.core.runtime.Status;
 
  16 import net.sourceforge.phpdt.internal.corext.util.Strings;
 
  17 import net.sourceforge.phpdt.internal.ui.JavaStatusConstants;
 
  18 import org.eclipse.jface.text.BadLocationException;
 
  19 import org.eclipse.jface.text.DefaultLineTracker;
 
  20 import org.eclipse.jface.text.IDocument;
 
  21 import org.eclipse.jface.text.IDocumentListener;
 
  22 import org.eclipse.jface.text.ILineTracker;
 
  23 import org.eclipse.jface.text.IRegion;
 
  24 import org.eclipse.jface.util.Assert;
 
  26 //import org.eclipse.jdt.internal.ui.JavaPlugin;
 
  27 //import org.eclipse.jdt.internal.ui.JavaStatusConstants;
 
  30  * An implementation of a <code>TextBuffer</code> that is based on <code>ITextSelection</code>
 
  31  * and <code>IDocument</code>.
 
  33 public class TextBuffer {
 
  35         private static class DocumentRegion extends TextRegion {
 
  37                 public DocumentRegion(IRegion region) {
 
  40                 public int getOffset() {
 
  41                         return fRegion.getOffset();
 
  43                 public int getLength() {
 
  44                         return fRegion.getLength();
 
  49                 public String content;
 
  50                 public int offsetDelta;
 
  53         private IDocument fDocument;
 
  55         private static final TextBufferFactory fgFactory= new TextBufferFactory();
 
  57         TextBuffer(IDocument document) {
 
  59                 Assert.isNotNull(fDocument);
 
  63          * Returns the number of characters in this text buffer.
 
  65          * @return the number of characters in this text buffer
 
  67         public int getLength() {
 
  68                 return fDocument.getLength();
 
  72          * Returns the number of lines in this text buffer.
 
  74          * @return the number of lines in this text buffer
 
  76         public int getNumberOfLines() {
 
  77                 return fDocument.getNumberOfLines();
 
  81          * Returns the character at the given offset in this text buffer.
 
  83          * @param offset a text buffer offset
 
  84          * @return the character at the offset
 
  85          * @exception  IndexOutOfBoundsException  if the <code>offset</code> 
 
  86          *  argument is negative or not less than the length of this text buffer.
 
  88         public char getChar(int offset) {
 
  90                         return fDocument.getChar(offset);
 
  91                 } catch (BadLocationException e) {
 
  92                         throw new ArrayIndexOutOfBoundsException(e.getMessage());
 
  97          * Returns the whole content of the text buffer.
 
  99          * @return the whole content of the text buffer
 
 101         public String getContent() {
 
 102                 return fDocument.get();
 
 106          * Returns length characters starting from the specified position.
 
 108          * @return the characters specified by the given text region. Returns <code>
 
 109          *  null</code> if text range is illegal
 
 111         public String getContent(int start, int length) {
 
 113                         return fDocument.get(start, length);
 
 114                 } catch (BadLocationException e) {
 
 119         public Block getBlockContent(int start, int length, int tabWidth) {
 
 120                 Block result= new Block();
 
 121                 StringBuffer buffer= new StringBuffer();
 
 122                 int lineOffset= getLineInformationOfOffset(start).getOffset();
 
 123                 if (start > lineOffset) {
 
 124                         String line= getContent(lineOffset, start - lineOffset);
 
 125                         String indent= Strings.getIndentString(line, tabWidth);
 
 126                         result.offsetDelta= -indent.length();
 
 127                         buffer.append(indent);
 
 129                 final int end= start + length;
 
 130                 TextRegion region= getLineInformationOfOffset(end);
 
 131                 lineOffset= region.getOffset();
 
 132                 // Cursor is at beginning of next line
 
 133                 if (lineOffset == end) {
 
 134                         int lineNumber= getLineOfOffset(lineOffset);
 
 135                         if (lineNumber > 0) {
 
 136                                 length= length - getLineDelimiter(lineNumber - 1).length();
 
 139                 if (buffer.length() == 0) {
 
 140                         result.content= getContent(start, length);
 
 142                         buffer.append(getContent(start, length));
 
 143                         result.content= buffer.toString();
 
 149          * Returns the preferred line delimiter to be used for this text buffer.
 
 151          * @return the preferred line delimiter
 
 153         public String getLineDelimiter() {
 
 154                 String lineDelimiter= getLineDelimiter(0);
 
 155                 if (lineDelimiter == null)
 
 156                         lineDelimiter= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
 
 157                 return lineDelimiter;
 
 161          * Returns the line delimiter used for the given line number. Returns <code>
 
 162          * null</code> if the line number is out of range.
 
 164          * @return the line delimiter used by the given line number or <code>null</code>
 
 166         public String getLineDelimiter(int line) {
 
 168                         return fDocument.getLineDelimiter(line);
 
 169                 } catch (BadLocationException e) {
 
 175          * Returns the line for the given line number. If there isn't any line for
 
 176          * the given line number, <code>null</code> is returned.
 
 178          * @return the line for the given line number or <code>null</code>
 
 180         public String getLineContent(int line) {
 
 182                         IRegion region= fDocument.getLineInformation(line);
 
 183                         return fDocument.get(region.getOffset(), region.getLength());
 
 184                 } catch (BadLocationException e) {
 
 190          * Returns the line indent for the given line. If there isn't any line for the
 
 191          * given line number, <code>-1</code> is returned.
 
 193          * @return the line indent for the given line number of <code>-1</code>
 
 195         public int getLineIndent(int lineNumber, int tabWidth) {
 
 196                 return Strings.computeIndent(getLineContent(lineNumber), tabWidth);
 
 200          * Returns a region of the specified line. The region contains  the offset and the 
 
 201          * length of the line excluding the line's delimiter. Returns <code>null</code> 
 
 202          * if the line doesn't exist.
 
 204          * @param line the line of interest
 
 205          * @return a line description or <code>null</code> if the given line doesn't
 
 208         public TextRegion getLineInformation(int line) {
 
 210                         return new DocumentRegion(fDocument.getLineInformation(line));
 
 211                 } catch (BadLocationException e) {
 
 217          * Returns a line region of the specified offset.  The region contains the offset and 
 
 218          * the length of the line excluding the line's delimiter. Returns <code>null</code> 
 
 219          * if the line doesn't exist.
 
 221          * @param offset an offset into a line
 
 222          * @return a line description or <code>null</code> if the given line doesn't
 
 225         public TextRegion getLineInformationOfOffset(int offset) {
 
 227                         return new DocumentRegion(fDocument.getLineInformationOfOffset(offset));
 
 228                 } catch (BadLocationException e) {
 
 234          * Returns the line number that contains the given position. If there isn't any
 
 235          * line that contains the position, <code>null</code> is returned. The returned 
 
 236          * string is a copy and doesn't contain the line delimiter.
 
 238          * @return the line that contains the given offset or <code>null</code> if line
 
 241         public int getLineOfOffset(int offset) {
 
 243                         return fDocument.getLineOfOffset(offset);
 
 244                 } catch (BadLocationException e) {
 
 250          * Returns the line that contains the given position. If there isn't any
 
 251          * line that contains the position, <code>null</code> is returned. The returned 
 
 252          * string is a copy and doesn't contain the line delimiter.
 
 254          * @return the line that contains the given offset or <code>null</code> if line
 
 257         public String getLineContentOfOffset(int offset) {
 
 259                         IRegion region= fDocument.getLineInformationOfOffset(offset);
 
 260                         return fDocument.get(region.getOffset(), region.getLength());
 
 261                 } catch (BadLocationException e) {
 
 267          * Converts the text determined by the region [offset, length] into an array of lines. 
 
 268          * The lines are copies of the original lines and don't contain any line delimiter 
 
 271          * @return the text converted into an array of strings. Returns <code>null</code> if the 
 
 272          *  region lies outside the source. 
 
 274         public String[] convertIntoLines(int offset, int length, boolean lastNewLineCreateEmptyLine) {
 
 276                         String text= fDocument.get(offset, length);
 
 277                         ILineTracker tracker= new DefaultLineTracker();
 
 279                         int size= tracker.getNumberOfLines();
 
 280                         int lastLine= size - 1;
 
 281                         List result= new ArrayList(size);
 
 282                         for (int i= 0; i < size; i++) {
 
 283                                 IRegion region= tracker.getLineInformation(i);
 
 284                                 String line= getContent(offset + region.getOffset(), region.getLength());
 
 285                                 if (i < lastLine || !"".equals(line) || lastNewLineCreateEmptyLine) //$NON-NLS-1$
 
 288                         return (String[]) result.toArray(new String[result.size()]);
 
 289                 } catch (BadLocationException e) {
 
 295          * Subsitutes the given text for the specified text position
 
 297          * @param offset the starting offset of the text to be replaced
 
 298          * @param length the length of the text to be replaced
 
 299          * @param text the substitution text
 
 300      * @exception  CoreException  if the text position [offset, length] is invalid.      
 
 302         public void replace(int offset, int length, String text) throws CoreException {
 
 304                         fDocument.replace(offset, length, text);
 
 305                 } catch (BadLocationException e) {
 
 306                         IStatus s = new Status(IStatus.ERROR, PHPeclipsePlugin.getPluginId(), JavaStatusConstants.INTERNAL_ERROR, 
 
 307                                 TextManipulationMessages.getFormattedString(
 
 308                                         "TextBuffer.wrongRange",  //$NON-NLS-1$
 
 309                                         new Object[] {new Integer(offset), new Integer(length) } ), e);
 
 310                         throw new CoreException(s);
 
 314         public void replace(TextRange range, String text) throws CoreException {
 
 315                 replace(range.fOffset, range.fLength, text);
 
 318         //---- Special methods used by the <code>TextBufferEditor</code>
 
 321          * Releases this text buffer.
 
 323         /* package */ void release() {
 
 326         /* package */ void registerUpdater(IDocumentListener listener) {
 
 327                 fDocument.addDocumentListener(listener);
 
 330         /* package */ void unregisterUpdater(IDocumentListener listener) {
 
 331                 fDocument.removeDocumentListener(listener);
 
 334         //---- Factory methods ----------------------------------------------------------------
 
 337          * Acquires a text buffer for the given file. If a text buffer for the given
 
 338          * file already exists, then that one is returned.
 
 340          * @param file the file for which a text buffer is requested
 
 341          * @return a managed text buffer for the given file
 
 342          * @exception CoreException if it was not possible to acquire the
 
 345         public static TextBuffer acquire(IFile file) throws CoreException {
 
 346                 return fgFactory.acquire(file);
 
 350          * Releases the given text buffer.
 
 352          * @param buffer the text buffer to be released
 
 354         public static void release(TextBuffer buffer) {
 
 355                 fgFactory.release(buffer);
 
 359          * Commits the changes made to the given text buffer to the underlying
 
 362          * @param buffer the text buffer containing the changes to be committed.
 
 363          * @param force if <code>true</code> the text buffer is committed in any case.
 
 364          *      If <code>false</code> the text buffer is <b>ONLY</b> committed if the client 
 
 365          *      is the last one that holds a reference to the text buffer. Clients of this
 
 366          *      method must make sure that they don't call this method from within an <code>
 
 367          *  IWorkspaceRunnable</code>.
 
 368          * @param pm the progress monitor used to report progress if committing is
 
 371         public static void commitChanges(TextBuffer buffer, boolean force, IProgressMonitor pm) throws CoreException {
 
 372                 fgFactory.commitChanges(buffer, force, pm);
 
 376          * Creates a new <code>TextBuffer</code> for the given file. The returned
 
 377          * buffer will not be managed. Any subsequent call to <code>create</code>
 
 378          * with the same file will return a different text buffer.
 
 380          * If the file is currently open in a text editor, the editors content is copied into
 
 381          * the returned <code>TextBuffer</code>. Otherwise the content is read from
 
 384          * @param file the file for which a text buffer is to be created
 
 385          * @return a new unmanaged text buffer
 
 386          * @exception CoreException if it was not possible to create the text buffer
 
 388         public static TextBuffer create(IFile file) throws CoreException {
 
 389                 return fgFactory.create(file);
 
 393          * Creates a new <code>TextBuffer</code> for the string. The returned
 
 394          * buffer will not be managed. Any subsequent call to <code>create</code>
 
 395          * with the identical string will return a different text buffer.
 
 397          * @param content the text buffer's content
 
 398          * @return a new unmanaged text buffer
 
 400         public static TextBuffer create(String content) {
 
 401                 return fgFactory.create(content);
 
 404         // Unclear which methods are needed if we get the new save model. If optimal no
 
 405         // save is needed at all.
 
 407         public static void save(TextBuffer buffer, IProgressMonitor pm) throws CoreException {
 
 408                 fgFactory.save(buffer, pm);
 
 411         public static void aboutToChange(TextBuffer buffer) throws CoreException {
 
 412                 fgFactory.aboutToChange(buffer);
 
 415         public static void changed(TextBuffer buffer) throws CoreException {
 
 416                 fgFactory.changed(buffer);