/*
* (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 org.eclipse.jdt.internal.ui.JavaPlugin;
//import org.eclipse.jdt.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);
}
}