/* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ package net.sourceforge.phpdt.internal.corext.textmanipulation; import java.util.ArrayList; import java.util.List; import net.sourceforge.phpdt.internal.corext.util.Strings; import net.sourceforge.phpdt.internal.ui.PHPStatusConstants; import net.sourceforge.phpeclipse.PHPeclipsePlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultLineTracker; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.ILineTracker; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.util.Assert; //import net.sourceforge.phpdt.internal.ui.JavaPlugin; //import net.sourceforge.phpdt.internal.ui.JavaStatusConstants; /** * An implementation of a TextBuffer that is based on ITextSelection * and IDocument. */ public class TextBuffer { private static class DocumentRegion extends TextRegion { IRegion fRegion; public DocumentRegion(IRegion region) { fRegion= region; } public int getOffset() { return fRegion.getOffset(); } public int getLength() { return fRegion.getLength(); } } public class Block { public String content; public int offsetDelta; } private IDocument fDocument; private static final TextBufferFactory fgFactory= new TextBufferFactory(); TextBuffer(IDocument document) { fDocument= document; Assert.isNotNull(fDocument); } /** * Returns the number of characters in this text buffer. * * @return the number of characters in this text buffer */ public int getLength() { return fDocument.getLength(); } /** * Returns the number of lines in this text buffer. * * @return the number of lines in this text buffer */ public int getNumberOfLines() { return fDocument.getNumberOfLines(); } /** * Returns the character at the given offset in this text buffer. * * @param offset a text buffer offset * @return the character at the offset * @exception IndexOutOfBoundsException if the offset * argument is negative or not less than the length of this text buffer. */ public char getChar(int offset) { try { return fDocument.getChar(offset); } catch (BadLocationException e) { throw new ArrayIndexOutOfBoundsException(e.getMessage()); } } /** * Returns the whole content of the text buffer. * * @return the whole content of the text buffer */ public String getContent() { return fDocument.get(); } /** * Returns length characters starting from the specified position. * * @return the characters specified by the given text region. Returns * null if text range is illegal */ public String getContent(int start, int length) { try { return fDocument.get(start, length); } catch (BadLocationException e) { return null; } } public Block getBlockContent(int start, int length, int tabWidth) { Block result= new Block(); StringBuffer buffer= new StringBuffer(); int lineOffset= getLineInformationOfOffset(start).getOffset(); if (start > lineOffset) { String line= getContent(lineOffset, start - lineOffset); String indent= Strings.getIndentString(line, tabWidth); result.offsetDelta= -indent.length(); buffer.append(indent); } final int end= start + length; TextRegion region= getLineInformationOfOffset(end); lineOffset= region.getOffset(); // Cursor is at beginning of next line if (lineOffset == end) { int lineNumber= getLineOfOffset(lineOffset); if (lineNumber > 0) { length= length - getLineDelimiter(lineNumber - 1).length(); } } if (buffer.length() == 0) { result.content= getContent(start, length); } else { buffer.append(getContent(start, length)); result.content= buffer.toString(); } return result; } /** * Returns the preferred line delimiter to be used for this text buffer. * * @return the preferred line delimiter */ public String getLineDelimiter() { String lineDelimiter= getLineDelimiter(0); if (lineDelimiter == null) lineDelimiter= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ return lineDelimiter; } /** * Returns the line delimiter used for the given line number. Returns * null if the line number is out of range. * * @return the line delimiter used by the given line number or null */ public String getLineDelimiter(int line) { try { return fDocument.getLineDelimiter(line); } catch (BadLocationException e) { return null; } } /** * Returns the line for the given line number. If there isn't any line for * the given line number, null is returned. * * @return the line for the given line number or null */ public String getLineContent(int line) { try { IRegion region= fDocument.getLineInformation(line); return fDocument.get(region.getOffset(), region.getLength()); } catch (BadLocationException e) { return null; } } /** * Returns the line indent for the given line. If there isn't any line for the * given line number, -1 is returned. * * @return the line indent for the given line number of -1 */ public int getLineIndent(int lineNumber, int tabWidth) { return Strings.computeIndent(getLineContent(lineNumber), tabWidth); } /** * Returns a region of the specified line. The region contains the offset and the * length of the line excluding the line's delimiter. Returns null * if the line doesn't exist. * * @param line the line of interest * @return a line description or null if the given line doesn't * exist */ public TextRegion getLineInformation(int line) { try { return new DocumentRegion(fDocument.getLineInformation(line)); } catch (BadLocationException e) { return null; } } /** * Returns a line region of the specified offset. The region contains the offset and * the length of the line excluding the line's delimiter. Returns null * if the line doesn't exist. * * @param offset an offset into a line * @return a line description or null if the given line doesn't * exist */ public TextRegion getLineInformationOfOffset(int offset) { try { return new DocumentRegion(fDocument.getLineInformationOfOffset(offset)); } catch (BadLocationException e) { return null; } } /** * Returns the line number that contains the given position. If there isn't any * line that contains the position, null is returned. The returned * string is a copy and doesn't contain the line delimiter. * * @return the line that contains the given offset or null if line * doesn't exist */ public int getLineOfOffset(int offset) { try { return fDocument.getLineOfOffset(offset); } catch (BadLocationException e) { return -1; } } /** * Returns the line that contains the given position. If there isn't any * line that contains the position, null is returned. The returned * string is a copy and doesn't contain the line delimiter. * * @return the line that contains the given offset or null if line * doesn't exist */ public String getLineContentOfOffset(int offset) { try { IRegion region= fDocument.getLineInformationOfOffset(offset); return fDocument.get(region.getOffset(), region.getLength()); } catch (BadLocationException e) { return null; } } /** * Converts the text determined by the region [offset, length] into an array of lines. * The lines are copies of the original lines and don't contain any line delimiter * characters. * * @return the text converted into an array of strings. Returns null if the * region lies outside the source. */ public String[] convertIntoLines(int offset, int length, boolean lastNewLineCreateEmptyLine) { try { String text= fDocument.get(offset, length); ILineTracker tracker= new DefaultLineTracker(); tracker.set(text); int size= tracker.getNumberOfLines(); int lastLine= size - 1; List result= new ArrayList(size); for (int i= 0; i < size; i++) { IRegion region= tracker.getLineInformation(i); String line= getContent(offset + region.getOffset(), region.getLength()); if (i < lastLine || !"".equals(line) || lastNewLineCreateEmptyLine) //$NON-NLS-1$ result.add(line); } return (String[]) result.toArray(new String[result.size()]); } catch (BadLocationException e) { return null; } } /** * Subsitutes the given text for the specified text position * * @param offset the starting offset of the text to be replaced * @param length the length of the text to be replaced * @param text the substitution text * @exception CoreException if the text position [offset, length] is invalid. */ public void replace(int offset, int length, String text) throws CoreException { try { fDocument.replace(offset, length, text); } catch (BadLocationException e) { IStatus s = new Status(IStatus.ERROR, PHPeclipsePlugin.getPluginId(), PHPStatusConstants.INTERNAL_ERROR, TextManipulationMessages.getFormattedString( "TextBuffer.wrongRange", //$NON-NLS-1$ new Object[] {new Integer(offset), new Integer(length) } ), e); throw new CoreException(s); } } public void replace(TextRange range, String text) throws CoreException { replace(range.fOffset, range.fLength, text); } //---- Special methods used by the TextBufferEditor /** * Releases this text buffer. */ /* package */ void release() { } /* package */ void registerUpdater(IDocumentListener listener) { fDocument.addDocumentListener(listener); } /* package */ void unregisterUpdater(IDocumentListener listener) { fDocument.removeDocumentListener(listener); } //---- Factory methods ---------------------------------------------------------------- /** * Acquires a text buffer for the given file. If a text buffer for the given * file already exists, then that one is returned. * * @param file the file for which a text buffer is requested * @return a managed text buffer for the given file * @exception CoreException if it was not possible to acquire the * text buffer */ public static TextBuffer acquire(IFile file) throws CoreException { return fgFactory.acquire(file); } /** * Releases the given text buffer. * * @param buffer the text buffer to be released */ public static void release(TextBuffer buffer) { fgFactory.release(buffer); } /** * Commits the changes made to the given text buffer to the underlying * storage system. * * @param buffer the text buffer containing the changes to be committed. * @param force if true the text buffer is committed in any case. * If false the text buffer is ONLY committed if the client * is the last one that holds a reference to the text buffer. Clients of this * method must make sure that they don't call this method from within an * IWorkspaceRunnable. * @param pm the progress monitor used to report progress if committing is * necessary */ public static void commitChanges(TextBuffer buffer, boolean force, IProgressMonitor pm) throws CoreException { fgFactory.commitChanges(buffer, force, pm); } /** * Creates a new TextBuffer for the given file. The returned * buffer will not be managed. Any subsequent call to create * with the same file will return a different text buffer. *

* If the file is currently open in a text editor, the editors content is copied into * the returned TextBuffer. Otherwise the content is read from * disk. * * @param file the file for which a text buffer is to be created * @return a new unmanaged text buffer * @exception CoreException if it was not possible to create the text buffer */ public static TextBuffer create(IFile file) throws CoreException { return fgFactory.create(file); } /** * Creates a new TextBuffer for the string. The returned * buffer will not be managed. Any subsequent call to create * with the identical string will return a different text buffer. * * @param content the text buffer's content * @return a new unmanaged text buffer */ public static TextBuffer create(String content) { return fgFactory.create(content); } // Unclear which methods are needed if we get the new save model. If optimal no // save is needed at all. public static void save(TextBuffer buffer, IProgressMonitor pm) throws CoreException { fgFactory.save(buffer, pm); } public static void aboutToChange(TextBuffer buffer) throws CoreException { fgFactory.aboutToChange(buffer); } public static void changed(TextBuffer buffer) throws CoreException { fgFactory.changed(buffer); } }