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);