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 net.sourceforge.phpdt.internal.ui.JavaPlugin;
28 // import net.sourceforge.phpdt.internal.ui.JavaStatusConstants;
31 * An implementation of a <code>TextBuffer</code> that is based on
32 * <code>ITextSelection</code> and <code>IDocument</code>.
34 public class TextBuffer {
36 private static class DocumentRegion extends TextRegion {
39 public DocumentRegion(IRegion region) {
43 public int getOffset() {
44 return fRegion.getOffset();
47 public int getLength() {
48 return fRegion.getLength();
53 public String content;
55 public int offsetDelta;
58 private IDocument fDocument;
60 private static final TextBufferFactory fgFactory = new TextBufferFactory();
62 TextBuffer(IDocument document) {
64 Assert.isNotNull(fDocument);
68 * Returns the number of characters in this text buffer.
70 * @return the number of characters in this text buffer
72 public int getLength() {
73 return fDocument.getLength();
77 * Returns the number of lines in this text buffer.
79 * @return the number of lines in this text buffer
81 public int getNumberOfLines() {
82 return fDocument.getNumberOfLines();
86 * Returns the character at the given offset in this text buffer.
89 * a text buffer offset
90 * @return the character at the offset
91 * @exception IndexOutOfBoundsException
92 * if the <code>offset</code> argument is negative or not
93 * less than the length of this text buffer.
95 public char getChar(int offset) {
97 return fDocument.getChar(offset);
98 } catch (BadLocationException e) {
99 throw new ArrayIndexOutOfBoundsException(e.getMessage());
104 * Returns the whole content of the text buffer.
106 * @return the whole content of the text buffer
108 public String getContent() {
109 return fDocument.get();
113 * Returns length characters starting from the specified position.
115 * @return the characters specified by the given text region. Returns <code>
117 * if text range is illegal
119 public String getContent(int start, int length) {
121 return fDocument.get(start, length);
122 } catch (BadLocationException e) {
127 public Block getBlockContent(int start, int length, int tabWidth) {
128 Block result = new Block();
129 StringBuffer buffer = new StringBuffer();
130 int lineOffset = getLineInformationOfOffset(start).getOffset();
131 if (start > lineOffset) {
132 String line = getContent(lineOffset, start - lineOffset);
133 String indent = Strings.getIndentString(line, tabWidth);
134 result.offsetDelta = -indent.length();
135 buffer.append(indent);
137 final int end = start + length;
138 TextRegion region = getLineInformationOfOffset(end);
139 lineOffset = region.getOffset();
140 // Cursor is at beginning of next line
141 if (lineOffset == end) {
142 int lineNumber = getLineOfOffset(lineOffset);
143 if (lineNumber > 0) {
144 length = length - getLineDelimiter(lineNumber - 1).length();
147 if (buffer.length() == 0) {
148 result.content = getContent(start, length);
150 buffer.append(getContent(start, length));
151 result.content = buffer.toString();
157 * Returns the preferred line delimiter to be used for this text buffer.
159 * @return the preferred line delimiter
161 public String getLineDelimiter() {
162 String lineDelimiter = getLineDelimiter(0);
163 if (lineDelimiter == null)
164 lineDelimiter = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
165 return lineDelimiter;
169 * Returns the line delimiter used for the given line number. Returns <code>
171 * if the line number is out of range.
173 * @return the line delimiter used by the given line number or
176 public String getLineDelimiter(int line) {
178 return fDocument.getLineDelimiter(line);
179 } catch (BadLocationException e) {
185 * Returns the line for the given line number. If there isn't any line for
186 * the given line number, <code>null</code> is returned.
188 * @return the line for the given line number or <code>null</code>
190 public String getLineContent(int line) {
192 IRegion region = fDocument.getLineInformation(line);
193 return fDocument.get(region.getOffset(), region.getLength());
194 } catch (BadLocationException e) {
200 * Returns the line indent for the given line. If there isn't any line for
201 * the given line number, <code>-1</code> is returned.
203 * @return the line indent for the given line number of <code>-1</code>
205 public int getLineIndent(int lineNumber, int tabWidth) {
206 return Strings.computeIndent(getLineContent(lineNumber), tabWidth);
210 * Returns a region of the specified line. The region contains the offset
211 * and the length of the line excluding the line's delimiter. Returns
212 * <code>null</code> if the line doesn't exist.
215 * the line of interest
216 * @return a line description or <code>null</code> if the given line
219 public TextRegion getLineInformation(int line) {
221 return new DocumentRegion(fDocument.getLineInformation(line));
222 } catch (BadLocationException e) {
228 * Returns a line region of the specified offset. The region contains the
229 * offset and the length of the line excluding the line's delimiter. Returns
230 * <code>null</code> if the line doesn't exist.
233 * an offset into a line
234 * @return a line description or <code>null</code> if the given line
237 public TextRegion getLineInformationOfOffset(int offset) {
239 return new DocumentRegion(fDocument
240 .getLineInformationOfOffset(offset));
241 } catch (BadLocationException e) {
247 * Returns the line number that contains the given position. If there isn't
248 * any line that contains the position, <code>null</code> is returned. The
249 * returned string is a copy and doesn't contain the line delimiter.
251 * @return the line that contains the given offset or <code>null</code> if
254 public int getLineOfOffset(int offset) {
256 return fDocument.getLineOfOffset(offset);
257 } catch (BadLocationException e) {
263 * Returns the line that contains the given position. If there isn't any
264 * line that contains the position, <code>null</code> is returned. The
265 * returned string is a copy and doesn't contain the line delimiter.
267 * @return the line that contains the given offset or <code>null</code> if
270 public String getLineContentOfOffset(int offset) {
272 IRegion region = fDocument.getLineInformationOfOffset(offset);
273 return fDocument.get(region.getOffset(), region.getLength());
274 } catch (BadLocationException e) {
280 * Converts the text determined by the region [offset, length] into an array
281 * of lines. The lines are copies of the original lines and don't contain
282 * any line delimiter characters.
284 * @return the text converted into an array of strings. Returns
285 * <code>null</code> if the region lies outside the source.
287 public String[] convertIntoLines(int offset, int length,
288 boolean lastNewLineCreateEmptyLine) {
290 String text = fDocument.get(offset, length);
291 ILineTracker tracker = new DefaultLineTracker();
293 int size = tracker.getNumberOfLines();
294 int lastLine = size - 1;
295 List result = new ArrayList(size);
296 for (int i = 0; i < size; i++) {
297 IRegion region = tracker.getLineInformation(i);
298 String line = getContent(offset + region.getOffset(), region
301 || !"".equals(line) || lastNewLineCreateEmptyLine) //$NON-NLS-1$
304 return (String[]) result.toArray(new String[result.size()]);
305 } catch (BadLocationException e) {
311 * Subsitutes the given text for the specified text position
314 * the starting offset of the text to be replaced
316 * the length of the text to be replaced
318 * the substitution text
319 * @exception CoreException
320 * if the text position [offset, length] is invalid.
322 public void replace(int offset, int length, String text)
323 throws CoreException {
325 fDocument.replace(offset, length, text);
326 } catch (BadLocationException e) {
327 IStatus s = new Status(IStatus.ERROR, PHPeclipsePlugin
328 .getPluginId(), PHPStatusConstants.INTERNAL_ERROR,
329 TextManipulationMessages.getFormattedString(
330 "TextBuffer.wrongRange", //$NON-NLS-1$
331 new Object[] { new Integer(offset),
332 new Integer(length) }), e);
333 throw new CoreException(s);
337 public void replace(TextRange range, String text) throws CoreException {
338 replace(range.fOffset, range.fLength, text);
341 // ---- Special methods used by the <code>TextBufferEditor</code>
344 * Releases this text buffer.
346 /* package */void release() {
349 /* package */void registerUpdater(IDocumentListener listener) {
350 fDocument.addDocumentListener(listener);
353 /* package */void unregisterUpdater(IDocumentListener listener) {
354 fDocument.removeDocumentListener(listener);
357 // ---- Factory methods
358 // ----------------------------------------------------------------
361 * Acquires a text buffer for the given file. If a text buffer for the given
362 * file already exists, then that one is returned.
365 * the file for which a text buffer is requested
366 * @return a managed text buffer for the given file
367 * @exception CoreException
368 * if it was not possible to acquire the text buffer
370 public static TextBuffer acquire(IFile file) throws CoreException {
371 return fgFactory.acquire(file);
375 * Releases the given text buffer.
378 * the text buffer to be released
380 public static void release(TextBuffer buffer) {
381 fgFactory.release(buffer);
385 * Commits the changes made to the given text buffer to the underlying
389 * the text buffer containing the changes to be committed.
391 * if <code>true</code> the text buffer is committed in any
392 * case. If <code>false</code> the text buffer is <b>ONLY</b>
393 * committed if the client is the last one that holds a reference
394 * to the text buffer. Clients of this method must make sure that
395 * they don't call this method from within an <code>
396 * IWorkspaceRunnable</code>.
398 * the progress monitor used to report progress if committing is
401 public static void commitChanges(TextBuffer buffer, boolean force,
402 IProgressMonitor pm) throws CoreException {
403 fgFactory.commitChanges(buffer, force, pm);
407 * Creates a new <code>TextBuffer</code> for the given file. The returned
408 * buffer will not be managed. Any subsequent call to <code>create</code>
409 * with the same file will return a different text buffer.
411 * If the file is currently open in a text editor, the editors content is
412 * copied into the returned <code>TextBuffer</code>. Otherwise the
413 * content is read from disk.
416 * the file for which a text buffer is to be created
417 * @return a new unmanaged text buffer
418 * @exception CoreException
419 * if it was not possible to create the text buffer
421 public static TextBuffer create(IFile file) throws CoreException {
422 return fgFactory.create(file);
426 * Creates a new <code>TextBuffer</code> for the string. The returned
427 * buffer will not be managed. Any subsequent call to <code>create</code>
428 * with the identical string will return a different text buffer.
431 * the text buffer's content
432 * @return a new unmanaged text buffer
434 public static TextBuffer create(String content) {
435 return fgFactory.create(content);
438 // Unclear which methods are needed if we get the new save model. If optimal
440 // save is needed at all.
442 public static void save(TextBuffer buffer, IProgressMonitor pm)
443 throws CoreException {
444 fgFactory.save(buffer, pm);
447 public static void aboutToChange(TextBuffer buffer) throws CoreException {
448 fgFactory.aboutToChange(buffer);
451 public static void changed(TextBuffer buffer) throws CoreException {
452 fgFactory.changed(buffer);