2 * (c) Copyright IBM Corp. 2000, 2001.
5 package net.sourceforge.phpdt.internal.corext.textmanipulation;
7 import java.util.ArrayList;
10 import net.sourceforge.phpdt.internal.corext.util.Strings;
11 import net.sourceforge.phpdt.internal.ui.PHPStatusConstants;
12 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
14 import org.eclipse.core.resources.IFile;
15 import org.eclipse.core.runtime.CoreException;
16 import org.eclipse.core.runtime.IProgressMonitor;
17 import org.eclipse.core.runtime.IStatus;
18 import org.eclipse.core.runtime.Status;
19 import org.eclipse.jface.text.BadLocationException;
20 import org.eclipse.jface.text.DefaultLineTracker;
21 import org.eclipse.jface.text.IDocument;
22 import org.eclipse.jface.text.IDocumentListener;
23 import org.eclipse.jface.text.ILineTracker;
24 import org.eclipse.jface.text.IRegion;
25 import org.eclipse.jface.util.Assert;
27 //import org.eclipse.jdt.internal.ui.JavaPlugin;
28 //import org.eclipse.jdt.internal.ui.JavaStatusConstants;
31 * An implementation of a <code>TextBuffer</code> that is based on <code>ITextSelection</code>
32 * and <code>IDocument</code>.
34 public class TextBuffer {
36 private static class DocumentRegion extends TextRegion {
38 public DocumentRegion(IRegion region) {
41 public int getOffset() {
42 return fRegion.getOffset();
44 public int getLength() {
45 return fRegion.getLength();
50 public String content;
51 public int offsetDelta;
54 private IDocument fDocument;
56 private static final TextBufferFactory fgFactory= new TextBufferFactory();
58 TextBuffer(IDocument document) {
60 Assert.isNotNull(fDocument);
64 * Returns the number of characters in this text buffer.
66 * @return the number of characters in this text buffer
68 public int getLength() {
69 return fDocument.getLength();
73 * Returns the number of lines in this text buffer.
75 * @return the number of lines in this text buffer
77 public int getNumberOfLines() {
78 return fDocument.getNumberOfLines();
82 * Returns the character at the given offset in this text buffer.
84 * @param offset a text buffer offset
85 * @return the character at the offset
86 * @exception IndexOutOfBoundsException if the <code>offset</code>
87 * argument is negative or not less than the length of this text buffer.
89 public char getChar(int offset) {
91 return fDocument.getChar(offset);
92 } catch (BadLocationException e) {
93 throw new ArrayIndexOutOfBoundsException(e.getMessage());
98 * Returns the whole content of the text buffer.
100 * @return the whole content of the text buffer
102 public String getContent() {
103 return fDocument.get();
107 * Returns length characters starting from the specified position.
109 * @return the characters specified by the given text region. Returns <code>
110 * null</code> if text range is illegal
112 public String getContent(int start, int length) {
114 return fDocument.get(start, length);
115 } catch (BadLocationException e) {
120 public Block getBlockContent(int start, int length, int tabWidth) {
121 Block result= new Block();
122 StringBuffer buffer= new StringBuffer();
123 int lineOffset= getLineInformationOfOffset(start).getOffset();
124 if (start > lineOffset) {
125 String line= getContent(lineOffset, start - lineOffset);
126 String indent= Strings.getIndentString(line, tabWidth);
127 result.offsetDelta= -indent.length();
128 buffer.append(indent);
130 final int end= start + length;
131 TextRegion region= getLineInformationOfOffset(end);
132 lineOffset= region.getOffset();
133 // Cursor is at beginning of next line
134 if (lineOffset == end) {
135 int lineNumber= getLineOfOffset(lineOffset);
136 if (lineNumber > 0) {
137 length= length - getLineDelimiter(lineNumber - 1).length();
140 if (buffer.length() == 0) {
141 result.content= getContent(start, length);
143 buffer.append(getContent(start, length));
144 result.content= buffer.toString();
150 * Returns the preferred line delimiter to be used for this text buffer.
152 * @return the preferred line delimiter
154 public String getLineDelimiter() {
155 String lineDelimiter= getLineDelimiter(0);
156 if (lineDelimiter == null)
157 lineDelimiter= System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
158 return lineDelimiter;
162 * Returns the line delimiter used for the given line number. Returns <code>
163 * null</code> if the line number is out of range.
165 * @return the line delimiter used by the given line number or <code>null</code>
167 public String getLineDelimiter(int line) {
169 return fDocument.getLineDelimiter(line);
170 } catch (BadLocationException e) {
176 * Returns the line for the given line number. If there isn't any line for
177 * the given line number, <code>null</code> is returned.
179 * @return the line for the given line number or <code>null</code>
181 public String getLineContent(int line) {
183 IRegion region= fDocument.getLineInformation(line);
184 return fDocument.get(region.getOffset(), region.getLength());
185 } catch (BadLocationException e) {
191 * Returns the line indent for the given line. If there isn't any line for the
192 * given line number, <code>-1</code> is returned.
194 * @return the line indent for the given line number of <code>-1</code>
196 public int getLineIndent(int lineNumber, int tabWidth) {
197 return Strings.computeIndent(getLineContent(lineNumber), tabWidth);
201 * Returns a region of the specified line. The region contains the offset and the
202 * length of the line excluding the line's delimiter. Returns <code>null</code>
203 * if the line doesn't exist.
205 * @param line the line of interest
206 * @return a line description or <code>null</code> if the given line doesn't
209 public TextRegion getLineInformation(int line) {
211 return new DocumentRegion(fDocument.getLineInformation(line));
212 } catch (BadLocationException e) {
218 * Returns a line region of the specified offset. The region contains the offset and
219 * the length of the line excluding the line's delimiter. Returns <code>null</code>
220 * if the line doesn't exist.
222 * @param offset an offset into a line
223 * @return a line description or <code>null</code> if the given line doesn't
226 public TextRegion getLineInformationOfOffset(int offset) {
228 return new DocumentRegion(fDocument.getLineInformationOfOffset(offset));
229 } catch (BadLocationException e) {
235 * Returns the line number that contains the given position. If there isn't any
236 * line that contains the position, <code>null</code> is returned. The returned
237 * string is a copy and doesn't contain the line delimiter.
239 * @return the line that contains the given offset or <code>null</code> if line
242 public int getLineOfOffset(int offset) {
244 return fDocument.getLineOfOffset(offset);
245 } catch (BadLocationException e) {
251 * Returns the line that contains the given position. If there isn't any
252 * line that contains the position, <code>null</code> is returned. The returned
253 * string is a copy and doesn't contain the line delimiter.
255 * @return the line that contains the given offset or <code>null</code> if line
258 public String getLineContentOfOffset(int offset) {
260 IRegion region= fDocument.getLineInformationOfOffset(offset);
261 return fDocument.get(region.getOffset(), region.getLength());
262 } catch (BadLocationException e) {
268 * Converts the text determined by the region [offset, length] into an array of lines.
269 * The lines are copies of the original lines and don't contain any line delimiter
272 * @return the text converted into an array of strings. Returns <code>null</code> if the
273 * region lies outside the source.
275 public String[] convertIntoLines(int offset, int length, boolean lastNewLineCreateEmptyLine) {
277 String text= fDocument.get(offset, length);
278 ILineTracker tracker= new DefaultLineTracker();
280 int size= tracker.getNumberOfLines();
281 int lastLine= size - 1;
282 List result= new ArrayList(size);
283 for (int i= 0; i < size; i++) {
284 IRegion region= tracker.getLineInformation(i);
285 String line= getContent(offset + region.getOffset(), region.getLength());
286 if (i < lastLine || !"".equals(line) || lastNewLineCreateEmptyLine) //$NON-NLS-1$
289 return (String[]) result.toArray(new String[result.size()]);
290 } catch (BadLocationException e) {
296 * Subsitutes the given text for the specified text position
298 * @param offset the starting offset of the text to be replaced
299 * @param length the length of the text to be replaced
300 * @param text the substitution text
301 * @exception CoreException if the text position [offset, length] is invalid.
303 public void replace(int offset, int length, String text) throws CoreException {
305 fDocument.replace(offset, length, text);
306 } catch (BadLocationException e) {
307 IStatus s = new Status(IStatus.ERROR, PHPeclipsePlugin.getPluginId(), PHPStatusConstants.INTERNAL_ERROR,
308 TextManipulationMessages.getFormattedString(
309 "TextBuffer.wrongRange", //$NON-NLS-1$
310 new Object[] {new Integer(offset), new Integer(length) } ), e);
311 throw new CoreException(s);
315 public void replace(TextRange range, String text) throws CoreException {
316 replace(range.fOffset, range.fLength, text);
319 //---- Special methods used by the <code>TextBufferEditor</code>
322 * Releases this text buffer.
324 /* package */ void release() {
327 /* package */ void registerUpdater(IDocumentListener listener) {
328 fDocument.addDocumentListener(listener);
331 /* package */ void unregisterUpdater(IDocumentListener listener) {
332 fDocument.removeDocumentListener(listener);
335 //---- Factory methods ----------------------------------------------------------------
338 * Acquires a text buffer for the given file. If a text buffer for the given
339 * file already exists, then that one is returned.
341 * @param file the file for which a text buffer is requested
342 * @return a managed text buffer for the given file
343 * @exception CoreException if it was not possible to acquire the
346 public static TextBuffer acquire(IFile file) throws CoreException {
347 return fgFactory.acquire(file);
351 * Releases the given text buffer.
353 * @param buffer the text buffer to be released
355 public static void release(TextBuffer buffer) {
356 fgFactory.release(buffer);
360 * Commits the changes made to the given text buffer to the underlying
363 * @param buffer the text buffer containing the changes to be committed.
364 * @param force if <code>true</code> the text buffer is committed in any case.
365 * If <code>false</code> the text buffer is <b>ONLY</b> committed if the client
366 * is the last one that holds a reference to the text buffer. Clients of this
367 * method must make sure that they don't call this method from within an <code>
368 * IWorkspaceRunnable</code>.
369 * @param pm the progress monitor used to report progress if committing is
372 public static void commitChanges(TextBuffer buffer, boolean force, IProgressMonitor pm) throws CoreException {
373 fgFactory.commitChanges(buffer, force, pm);
377 * Creates a new <code>TextBuffer</code> for the given file. The returned
378 * buffer will not be managed. Any subsequent call to <code>create</code>
379 * with the same file will return a different text buffer.
381 * If the file is currently open in a text editor, the editors content is copied into
382 * the returned <code>TextBuffer</code>. Otherwise the content is read from
385 * @param file the file for which a text buffer is to be created
386 * @return a new unmanaged text buffer
387 * @exception CoreException if it was not possible to create the text buffer
389 public static TextBuffer create(IFile file) throws CoreException {
390 return fgFactory.create(file);
394 * Creates a new <code>TextBuffer</code> for the string. The returned
395 * buffer will not be managed. Any subsequent call to <code>create</code>
396 * with the identical string will return a different text buffer.
398 * @param content the text buffer's content
399 * @return a new unmanaged text buffer
401 public static TextBuffer create(String content) {
402 return fgFactory.create(content);
405 // Unclear which methods are needed if we get the new save model. If optimal no
406 // save is needed at all.
408 public static void save(TextBuffer buffer, IProgressMonitor pm) throws CoreException {
409 fgFactory.save(buffer, pm);
412 public static void aboutToChange(TextBuffer buffer) throws CoreException {
413 fgFactory.aboutToChange(buffer);
416 public static void changed(TextBuffer buffer) throws CoreException {
417 fgFactory.changed(buffer);