blob: 2080cc5f831e83dcad1cd05b01b16d9ec65aad04 [file] [log] [blame]
package org.eclipse.cdt.internal.ui.editor;
/**********************************************************************
* Copyright (c) 2002,2003 Rational Software Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* Rational Software - Initial API and implementation
***********************************************************************/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.cdt.core.model.BufferChangedEvent;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.IBuffer;
import org.eclipse.cdt.core.model.IBufferChangedListener;
import org.eclipse.cdt.core.model.IOpenable;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
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.swt.widgets.Display;
/**
* Adapts <code>IDocument</code> to <code>IBuffer</code>. Uses the
* same algorithm as the text widget to determine the buffer's line delimiter.
* All text inserted into the buffer is converted to this line delimiter.
* This class is <code>public</code> for test purposes only.
*
* This class is similar to the JDT DocumentAdapter class.
*/
public class DocumentAdapter implements IBuffer, IDocumentListener {
/**
* Internal implementation of a NULL instanceof IBuffer.
*/
static private class NullBuffer implements IBuffer {
public void addBufferChangedListener(IBufferChangedListener listener) {}
public void append(char[] text) {}
public void append(String text) {}
public void close() {}
public char getChar(int position) {
return 0;
}
public char[] getCharacters() {
return null;
}
public String getContents() {
return null;
}
public int getLength() {
return 0;
}
public IOpenable getOwner() {
return null;
}
public String getText(int offset, int length) {
return null;
}
public IResource getUnderlyingResource() {
return null;
}
public boolean hasUnsavedChanges() {
return false;
}
public boolean isClosed() {
return false;
}
public boolean isReadOnly() {
return true;
}
public void removeBufferChangedListener(IBufferChangedListener listener) {}
public void replace(int position, int length, char[] text) {}
public void replace(int position, int length, String text) {}
public void save(IProgressMonitor progress, boolean force) throws CModelException {}
public void setContents(char[] contents) {}
public void setContents(String contents) {}
};
/** NULL implementing <code>IBuffer</code> */
public final static IBuffer NULL= new NullBuffer();
/*
* @see IBuffer#addBufferChangedListener(IBufferChangedListener)
*/
public void addBufferChangedListener(IBufferChangedListener listener) {
Assert.isNotNull(listener);
if (!fBufferListeners.contains(listener))
fBufferListeners.add(listener);
}
/**
* Executes a document set content call in the ui thread.
*/
protected class DocumentSetCommand implements Runnable {
private String fContents;
public void run() {
fDocument.set(fContents);
}
public void set(String contents) {
fContents= contents;
Display.getDefault().syncExec(this);
}
};
/**
* Executes a document replace call in the ui thread.
*/
protected class DocumentReplaceCommand implements Runnable {
private int fOffset;
private int fLength;
private String fText;
public void run() {
try {
fDocument.replace(fOffset, fLength, fText);
} catch (BadLocationException x) {
// ignore
}
}
public void replace(int offset, int length, String text) {
fOffset= offset;
fLength= length;
fText= text;
Display.getDefault().syncExec(this);
}
};
private IOpenable fOwner;
IDocument fDocument;
private DocumentSetCommand fSetCmd= new DocumentSetCommand();
private DocumentReplaceCommand fReplaceCmd= new DocumentReplaceCommand();
private Object fProviderKey;
private CDocumentProvider fProvider;
private String fLineDelimiter;
private ILineTracker fLineTracker;
private List fBufferListeners= new ArrayList(3);
private IStatus fStatus;
public DocumentAdapter(IOpenable owner, IDocument document, ILineTracker lineTracker, CDocumentProvider provider, Object providerKey) {
Assert.isNotNull(document);
Assert.isNotNull(lineTracker);
fOwner= owner;
fDocument= document;
fLineTracker= lineTracker;
fProvider= provider;
fProviderKey= providerKey;
fDocument.addPrenotifiedDocumentListener(this);
}
/**
* Sets the status of this document adapter.
*/
public void setStatus(IStatus status) {
fStatus= status;
}
/**
* Returns the status of this document adapter.
*/
public IStatus getStatus() {
return fStatus;
}
/**
* Returns the adapted document.
*
* @return the adapted document
*/
public IDocument getDocument() {
return fDocument;
}
/**
* Returns the line delimiter of this buffer. As a document has a set of
* valid line delimiters, this set must be reduced to size 1.
*/
protected String getLineDelimiter() {
if (fLineDelimiter == null) {
try {
fLineDelimiter= fDocument.getLineDelimiter(0);
} catch (BadLocationException x) {
}
if (fLineDelimiter == null) {
/*
* Follow up fix for: 1GF5UU0: ITPJUI:WIN2000 - "Organize Imports" in java editor inserts lines in wrong format
* The line delimiter must always be a legal document line delimiter.
*/
String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
String[] delimiters= fDocument.getLegalLineDelimiters();
Assert.isTrue(delimiters.length > 0);
for (int i= 0; i < delimiters.length; i++) {
if (delimiters[i].equals(sysLineDelimiter)) {
fLineDelimiter= sysLineDelimiter;
break;
}
}
if (fLineDelimiter == null) {
// system line delimiter is not a legal document line delimiter
fLineDelimiter= delimiters[0];
}
}
}
return fLineDelimiter;
}
/**
* Converts the given string to the line delimiter of this buffer.
* This method is <code>public</code> for test purposes only.
*/
public String normalize(String text) {
fLineTracker.set(text);
int lines= fLineTracker.getNumberOfLines();
if (lines <= 1)
return text;
StringBuffer buffer= new StringBuffer(text);
try {
IRegion previous= fLineTracker.getLineInformation(0);
for (int i= 1; i < lines; i++) {
int lastLineEnd= previous.getOffset() + previous.getLength();
int lineStart= fLineTracker.getLineInformation(i).getOffset();
fLineTracker.replace(lastLineEnd, lineStart - lastLineEnd, getLineDelimiter());
buffer.replace(lastLineEnd, lineStart, getLineDelimiter());
previous= fLineTracker.getLineInformation(i);
}
// last line
String delimiter= fLineTracker.getLineDelimiter(lines -1);
if (delimiter != null && delimiter.length() > 0)
buffer.replace(previous.getOffset() + previous.getLength(), buffer.length(), getLineDelimiter());
return buffer.toString();
} catch (BadLocationException x) {
}
return text;
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#append(char)
*/
public void append(char[] text) {
append(new String(text));
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#append(java.lang.String)
*/
public void append(String text) {
fReplaceCmd.replace(fDocument.getLength(), 0, normalize(text));
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#close()
*/
public void close() {
if (isClosed())
return;
IDocument d= fDocument;
fDocument= null;
d.removePrenotifiedDocumentListener(this);
fireBufferChanged(new BufferChangedEvent(this, 0, 0, null));
fBufferListeners.clear();
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getChar(int)
*/
public char getChar(int position) {
try {
return fDocument.getChar(position);
} catch (BadLocationException x) {
throw new ArrayIndexOutOfBoundsException();
}
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getCharacters()
*/
public char[] getCharacters() {
String content= getContents();
return content == null ? null : content.toCharArray();
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getContents()
*/
public String getContents() {
return fDocument.get();
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getLength()
*/
public int getLength() {
return fDocument.getLength();
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getOwner()
*/
public IOpenable getOwner() {
return (IOpenable) fOwner;
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getText(int, int)
*/
public String getText(int offset, int length) {
try {
return fDocument.get(offset, length);
} catch (BadLocationException x) {
throw new ArrayIndexOutOfBoundsException();
}
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#getUnderlyingResource()
*/
public IResource getUnderlyingResource() {
//return null;
return fProvider != null ? fProvider.getUnderlyingResource(fProviderKey) : null;
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#hasUnsavedChanges()
*/
public boolean hasUnsavedChanges() {
//return false;
return fProvider != null ? fProvider.canSaveDocument(fProviderKey) : false;
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#isClosed()
*/
public boolean isClosed() {
return fDocument == null;
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#isReadOnly()
*/
public boolean isReadOnly() {
//return false;
IResource resource= getUnderlyingResource();
return resource == null ? true : resource.isReadOnly();
}
/*
* @see IBuffer#removeBufferChangedListener(IBufferChangedListener)
*/
public void removeBufferChangedListener(IBufferChangedListener listener) {
Assert.isNotNull(listener);
fBufferListeners.remove(listener);
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#replace(int, int, char)
*/
public void replace(int position, int length, char[] text) {
replace(position, length, new String(text));
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#replace(int, int, java.lang.String)
*/
public void replace(int position, int length, String text) {
fReplaceCmd.replace(position, length, normalize(text));
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#save(org.eclipse.core.runtime.IProgressMonitor, boolean)
*/
public void save(IProgressMonitor progress, boolean force) throws CModelException {
if (fProvider != null) {
try {
fProvider.saveDocumentContent(progress, fProviderKey, fDocument, force);
} catch (CoreException e) {
throw new CModelException(e);
}
}
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#setContents(char)
*/
public void setContents(char[] contents) {
setContents(new String(contents));
}
/**
* @see org.eclipse.cdt.internal.core.model.IBuffer#setContents(java.lang.String)
*/
public void setContents(String contents) {
int oldLength= fDocument.getLength();
if (contents == null) {
if (oldLength != 0)
fSetCmd.set(""); //$NON-NLS-1$
} else {
// set only if different
String newContents= normalize(contents);
int newLength= newContents.length();
if (oldLength != newLength || !newContents.equals(fDocument.get()))
fSetCmd.set(newContents);
}
}
/*
* @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
*/
public void documentAboutToBeChanged(DocumentEvent event) {
// there is nothing to do here
}
/*
* @see IDocumentListener#documentChanged(DocumentEvent)
*/
public void documentChanged(DocumentEvent event) {
fireBufferChanged(new BufferChangedEvent(this, event.getOffset(), event.getLength(), event.getText()));
}
private void fireBufferChanged(BufferChangedEvent event) {
if (fBufferListeners != null && fBufferListeners.size() > 0) {
Iterator e= new ArrayList(fBufferListeners).iterator();
while (e.hasNext())
((IBufferChangedListener) e.next()).bufferChanged(event);
}
}
}