1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  12 package net.sourceforge.phpeclipse.phpeditor;
 
  15 import java.util.ArrayList;
 
  16 import java.util.Iterator;
 
  17 import java.util.List;
 
  19 import net.sourceforge.phpdt.core.BufferChangedEvent;
 
  20 import net.sourceforge.phpdt.core.IBuffer;
 
  21 import net.sourceforge.phpdt.core.IBufferChangedListener;
 
  22 import net.sourceforge.phpdt.core.IOpenable;
 
  23 import net.sourceforge.phpdt.core.JavaModelException;
 
  25 import org.eclipse.core.resources.IResource;
 
  26 import org.eclipse.core.runtime.CoreException;
 
  27 import org.eclipse.core.runtime.IProgressMonitor;
 
  28 import org.eclipse.core.runtime.IStatus;
 
  29 import org.eclipse.jface.text.Assert;
 
  30 import org.eclipse.jface.text.BadLocationException;
 
  31 import org.eclipse.jface.text.DocumentEvent;
 
  32 import org.eclipse.jface.text.IDocument;
 
  33 import org.eclipse.jface.text.IDocumentListener;
 
  34 import org.eclipse.jface.text.ILineTracker;
 
  35 import org.eclipse.jface.text.IRegion;
 
  36 import org.eclipse.swt.widgets.Display;
 
  41  * Adapts <code>IDocument</code> to <code>IBuffer</code>. Uses the
 
  42  * same algorithm as the text widget to determine the buffer's line delimiter. 
 
  43  * All text inserted into the buffer is converted to this line delimiter.
 
  44  * This class is <code>public</code> for test purposes only.
 
  46 public class DocumentAdapter implements IBuffer, IDocumentListener {
 
  49                  * Internal implementation of a NULL instanceof IBuffer.
 
  51                 static private class NullBuffer implements IBuffer {
 
  53                         public void addBufferChangedListener(IBufferChangedListener listener) {}
 
  55                         public void append(char[] text) {}
 
  57                         public void append(String text) {}
 
  59                         public void close() {}
 
  61                         public char getChar(int position) {
 
  65                         public char[] getCharacters() {
 
  69                         public String getContents() {
 
  73                         public int getLength() {
 
  77                         public IOpenable getOwner() {
 
  81                         public String getText(int offset, int length) {
 
  85                         public IResource getUnderlyingResource() {
 
  89                         public boolean hasUnsavedChanges() {
 
  93                         public boolean isClosed() {
 
  97                         public boolean isReadOnly() {
 
 101                         public void removeBufferChangedListener(IBufferChangedListener listener) {}
 
 103                         public void replace(int position, int length, char[] text) {}
 
 105                         public void replace(int position, int length, String text) {}
 
 107                         public void save(IProgressMonitor progress, boolean force) throws JavaModelException {}
 
 109                         public void setContents(char[] contents) {}
 
 111                         public void setContents(String contents) {}
 
 115         /** NULL implementing <code>IBuffer</code> */
 
 116         public final static IBuffer NULL= new NullBuffer();
 
 120          *  Executes a document set content call in the ui thread.
 
 122         protected class DocumentSetCommand implements Runnable {
 
 124                 private String fContents;
 
 127                         fDocument.set(fContents);
 
 130                 public void set(String contents) {
 
 132                         Display.getDefault().syncExec(this);
 
 137          * Executes a document replace call in the ui thread.
 
 139         protected class DocumentReplaceCommand implements Runnable {
 
 143                 private String fText;
 
 147                                 fDocument.replace(fOffset, fLength, fText);
 
 148                         } catch (BadLocationException x) {
 
 153                 public void replace(int offset, int length, String text) {
 
 157                         Display.getDefault().syncExec(this);
 
 161         private IOpenable fOwner;
 
 162         private IDocument fDocument;
 
 163         private DocumentSetCommand fSetCmd= new DocumentSetCommand();
 
 164         private DocumentReplaceCommand fReplaceCmd= new DocumentReplaceCommand();
 
 166         private Object fProviderKey;
 
 167         private PHPDocumentProvider fProvider;
 
 168         private String fLineDelimiter;
 
 169         private ILineTracker fLineTracker;
 
 171         private List fBufferListeners= new ArrayList(3);
 
 173         private IStatus fStatus;
 
 176          * This method is <code>public</code> for test purposes only.
 
 178         public DocumentAdapter(IOpenable owner, IDocument document, ILineTracker lineTracker, PHPDocumentProvider provider, Object providerKey) {
 
 180                 Assert.isNotNull(document);
 
 181                 Assert.isNotNull(lineTracker);
 
 185                 fLineTracker= lineTracker;
 
 187                 fProviderKey= providerKey;
 
 189                 fDocument.addPrenotifiedDocumentListener(this);
 
 193          * Sets the status of this document adapter.
 
 195         public void setStatus(IStatus status) {
 
 200          * Returns the status of this document adapter.
 
 202         public IStatus getStatus() {
 
 207          * Returns the adapted document.
 
 209          * @return the adapted document
 
 211         public IDocument getDocument() {
 
 216          * Returns the line delimiter of this buffer. As a document has a set of
 
 217          * valid line delimiters, this set must be reduced to size 1.
 
 219         protected String getLineDelimiter() {
 
 221                 if (fLineDelimiter == null) {
 
 224                                 fLineDelimiter= fDocument.getLineDelimiter(0);
 
 225                         } catch (BadLocationException x) {
 
 228                         if (fLineDelimiter == null) {
 
 230                                  * Follow up fix for: 1GF5UU0: ITPJUI:WIN2000 - "Organize Imports" in java editor inserts lines in wrong format
 
 231                                  * The line delimiter must always be a legal document line delimiter.
 
 233                                 String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$
 
 234                                 String[] delimiters= fDocument.getLegalLineDelimiters();
 
 235                                 Assert.isTrue(delimiters.length > 0);
 
 236                                 for (int i= 0; i < delimiters.length; i++) {
 
 237                                         if (delimiters[i].equals(sysLineDelimiter)) {
 
 238                                                 fLineDelimiter= sysLineDelimiter;
 
 243                                 if (fLineDelimiter == null) {
 
 244                                         // system line delimiter is not a legal document line delimiter
 
 245                                         fLineDelimiter= delimiters[0];
 
 250                 return fLineDelimiter;
 
 254          * Converts the given string to the line delimiter of this buffer.
 
 255          * This method is <code>public</code> for test purposes only.
 
 257         public String normalize(String text) {
 
 258                 fLineTracker.set(text);
 
 260                 int lines= fLineTracker.getNumberOfLines();
 
 264                 StringBuffer buffer= new StringBuffer(text);
 
 267                         IRegion previous= fLineTracker.getLineInformation(0);
 
 268                         for (int i= 1; i < lines; i++) {
 
 269                                 int lastLineEnd= previous.getOffset() + previous.getLength();
 
 270                                 int lineStart= fLineTracker.getLineInformation(i).getOffset();
 
 271                                 fLineTracker.replace(lastLineEnd,  lineStart - lastLineEnd, getLineDelimiter());
 
 272                                 buffer.replace(lastLineEnd, lineStart, getLineDelimiter());
 
 273                                 previous= fLineTracker.getLineInformation(i);
 
 277                         String delimiter= fLineTracker.getLineDelimiter(lines -1);
 
 278                         if (delimiter != null && delimiter.length() > 0)
 
 279                                 buffer.replace(previous.getOffset() + previous.getLength(), buffer.length(), getLineDelimiter());
 
 281                         return buffer.toString();
 
 282                 } catch (BadLocationException x) {
 
 289          * @see IBuffer#addBufferChangedListener(IBufferChangedListener)
 
 291         public void addBufferChangedListener(IBufferChangedListener listener) {
 
 292                 Assert.isNotNull(listener);
 
 293                 if (!fBufferListeners.contains(listener))
 
 294                         fBufferListeners.add(listener);
 
 298          * @see IBuffer#removeBufferChangedListener(IBufferChangedListener)
 
 300         public void removeBufferChangedListener(IBufferChangedListener listener) {
 
 301                 Assert.isNotNull(listener);
 
 302                 fBufferListeners.remove(listener);
 
 306          * @see IBuffer#append(char[])
 
 308         public void append(char[] text) {
 
 309                 append(new String(text));
 
 313          * @see IBuffer#append(String) 
 
 315         public void append(String text) {
 
 316                 fReplaceCmd.replace(fDocument.getLength(), 0, normalize(text));
 
 320          * @see IBuffer#close()
 
 322         public void close() {
 
 327                 IDocument d= fDocument;
 
 329                 d.removePrenotifiedDocumentListener(this);
 
 331                 fireBufferChanged(new BufferChangedEvent(this, 0, 0, null));
 
 332                 fBufferListeners.clear();
 
 336          * @see IBuffer#getChar(int)
 
 338         public char getChar(int position) {
 
 340                         return fDocument.getChar(position);
 
 341                 } catch (BadLocationException x) {
 
 342                         throw new ArrayIndexOutOfBoundsException();
 
 347          *  @see IBuffer#getCharacters()
 
 349         public char[] getCharacters() {
 
 350                 String content= getContents();
 
 351                 return content == null ? null : content.toCharArray();
 
 355          * @see IBuffer#getContents()
 
 357         public String getContents() {
 
 358                 return fDocument.get();
 
 362          * @see IBuffer#getLength()
 
 364         public int getLength() {
 
 365                 return fDocument.getLength();
 
 369          * @see IBuffer#getOwner()
 
 371         public IOpenable getOwner() {
 
 372                 return (IOpenable) fOwner;
 
 376          * @see IBuffer#getText(int, int)
 
 378         public String getText(int offset, int length) {
 
 380                         return fDocument.get(offset, length);
 
 381                 } catch (BadLocationException x) {
 
 382                         throw new ArrayIndexOutOfBoundsException();
 
 387          * @see IBuffer#getUnderlyingResource()
 
 389         public IResource getUnderlyingResource() {
 
 390                 return fProvider != null ? fProvider.getUnderlyingResource(fProviderKey) : null;
 
 394          * @see IBuffer#hasUnsavedChanges()
 
 396         public boolean hasUnsavedChanges() {
 
 397                 return fProvider != null ? fProvider.canSaveDocument(fProviderKey) : false;
 
 401          * @see IBuffer#isClosed()
 
 403         public boolean isClosed() {
 
 404                 return fDocument == null;
 
 408          * @see IBuffer#isReadOnly()
 
 410         public boolean isReadOnly() {
 
 411                 IResource resource= getUnderlyingResource();
 
 412                 return resource == null ? true : resource.isReadOnly();
 
 416          * @see IBuffer#replace(int, int, char[])
 
 418         public void replace(int position, int length, char[] text) {
 
 419                 replace(position, length, new String(text));
 
 423          * @see IBuffer#replace(int, int, String)
 
 425         public void replace(int position, int length, String text) {
 
 426                 fReplaceCmd.replace(position, length, normalize(text));
 
 430          * @see IBuffer#save(IProgressMonitor, boolean)
 
 432         public void save(IProgressMonitor progress, boolean force) throws JavaModelException {
 
 433                 if (fProvider != null) {
 
 435                                 fProvider.saveDocumentContent(progress, fProviderKey, fDocument, force);
 
 436                         } catch (CoreException e) {
 
 437                                 throw new JavaModelException(e);
 
 443          * @see IBuffer#setContents(char[])
 
 445         public void setContents(char[] contents) {
 
 446                 setContents(new String(contents));
 
 450          * @see IBuffer#setContents(String)
 
 452         public void setContents(String contents) {
 
 453                 int oldLength= fDocument.getLength();
 
 455                 if (contents == null) {
 
 458                                 fSetCmd.set(""); //$NON-NLS-1$
 
 462                         // set only if different
 
 463                         String newContents= normalize(contents);
 
 464                         int newLength= newContents.length();
 
 466                         if (oldLength != newLength || !newContents.equals(fDocument.get()))
 
 467                                 fSetCmd.set(newContents);
 
 472          * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
 
 474         public void documentAboutToBeChanged(DocumentEvent event) {
 
 475                 // there is nothing to do here
 
 479          * @see IDocumentListener#documentChanged(DocumentEvent)
 
 481         public void documentChanged(DocumentEvent event) {
 
 482                 fireBufferChanged(new BufferChangedEvent(this, event.getOffset(), event.getLength(), event.getText()));
 
 485         private void fireBufferChanged(BufferChangedEvent event) {
 
 486                 if (fBufferListeners != null && fBufferListeners.size() > 0) {
 
 487                         Iterator e= new ArrayList(fBufferListeners).iterator();
 
 489                                 ((IBufferChangedListener) e.next()).bufferChanged(event);