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.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
21 import net.sourceforge.phpdt.core.BufferChangedEvent;
22 import net.sourceforge.phpdt.core.IBuffer;
23 import net.sourceforge.phpdt.core.IBufferChangedListener;
24 import net.sourceforge.phpdt.core.IOpenable;
25 import net.sourceforge.phpdt.core.JavaModelException;
26 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
28 import org.eclipse.core.filebuffers.FileBuffers;
29 import org.eclipse.core.filebuffers.ITextFileBuffer;
30 import org.eclipse.core.filebuffers.ITextFileBufferManager;
31 import org.eclipse.core.resources.IFile;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.IPath;
35 import org.eclipse.core.runtime.IProgressMonitor;
36 import org.eclipse.core.runtime.IStatus;
37 import org.eclipse.core.runtime.NullProgressMonitor;
38 import org.eclipse.jface.text.Assert;
39 import org.eclipse.jface.text.BadLocationException;
40 import org.eclipse.jface.text.DefaultLineTracker;
41 import org.eclipse.jface.text.DocumentEvent;
42 import org.eclipse.jface.text.IDocument;
43 import org.eclipse.jface.text.IDocumentListener;
44 import org.eclipse.swt.widgets.Display;
49 * Adapts <code>IDocument</code> to <code>IBuffer</code>. Uses the
50 * same algorithm as the text widget to determine the buffer's line delimiter.
51 * All text inserted into the buffer is converted to this line delimiter.
52 * This class is <code>public</code> for test purposes only.
54 public class DocumentAdapter implements IBuffer, IDocumentListener {
57 * Internal implementation of a NULL instanceof IBuffer.
59 static private class NullBuffer implements IBuffer {
60 public void addBufferChangedListener(IBufferChangedListener listener) {}
61 public void append(char[] text) {}
62 public void append(String text) {}
63 public void close() {}
64 public char getChar(int position) { return 0; }
65 public char[] getCharacters() { return null; }
66 public String getContents() { return null; }
67 public int getLength() { return 0; }
68 public IOpenable getOwner() { return null; }
69 public String getText(int offset, int length) { return null; }
70 public IResource getUnderlyingResource() { return null; }
71 public boolean hasUnsavedChanges() { return false; }
72 public boolean isClosed() { return false; }
73 public boolean isReadOnly() { return true; }
74 public void removeBufferChangedListener(IBufferChangedListener listener) {}
75 public void replace(int position, int length, char[] text) {}
76 public void replace(int position, int length, String text) {}
77 public void save(IProgressMonitor progress, boolean force) throws JavaModelException {}
78 public void setContents(char[] contents) {}
79 public void setContents(String contents) {}
83 /** NULL implementing <code>IBuffer</code> */
84 public final static IBuffer NULL= new NullBuffer();
88 * Executes a document set content call in the ui thread.
90 protected class DocumentSetCommand implements Runnable {
92 private String fContents;
95 fDocument.set(fContents);
98 public void set(String contents) {
100 Display.getDefault().syncExec(this);
105 * Executes a document replace call in the ui thread.
107 protected class DocumentReplaceCommand implements Runnable {
111 private String fText;
115 fDocument.replace(fOffset, fLength, fText);
116 } catch (BadLocationException x) {
121 public void replace(int offset, int length, String text) {
125 Display.getDefault().syncExec(this);
129 private static final boolean DEBUG_LINE_DELIMITERS= true;
131 private IOpenable fOwner;
133 private ITextFileBuffer fTextFileBuffer;
134 private IDocument fDocument;
136 private DocumentSetCommand fSetCmd= new DocumentSetCommand();
137 private DocumentReplaceCommand fReplaceCmd= new DocumentReplaceCommand();
139 private Set fLegalLineDelimiters;
141 private List fBufferListeners= new ArrayList(3);
142 private IStatus fStatus;
146 * This method is <code>public</code> for test purposes only.
148 public DocumentAdapter(IOpenable owner, IFile file) {
156 private void initialize() {
157 ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
158 IPath location= fFile.getFullPath();
160 manager.connect(location, new NullProgressMonitor());
161 fTextFileBuffer= manager.getTextFileBuffer(location);
162 fDocument= fTextFileBuffer.getDocument();
163 } catch (CoreException x) {
164 fStatus= x.getStatus();
165 fDocument= manager.createEmptyDocument(location);
167 fDocument.addPrenotifiedDocumentListener(this);
171 * Returns the status of this document adapter.
173 public IStatus getStatus() {
176 if (fTextFileBuffer != null)
177 return fTextFileBuffer.getStatus();
182 * Returns the adapted document.
184 * @return the adapted document
186 public IDocument getDocument() {
191 * @see IBuffer#addBufferChangedListener(IBufferChangedListener)
193 public void addBufferChangedListener(IBufferChangedListener listener) {
194 Assert.isNotNull(listener);
195 if (!fBufferListeners.contains(listener))
196 fBufferListeners.add(listener);
200 * @see IBuffer#removeBufferChangedListener(IBufferChangedListener)
202 public void removeBufferChangedListener(IBufferChangedListener listener) {
203 Assert.isNotNull(listener);
204 fBufferListeners.remove(listener);
208 * @see IBuffer#append(char[])
210 public void append(char[] text) {
211 append(new String(text));
215 * @see IBuffer#append(String)
217 public void append(String text) {
218 if (DEBUG_LINE_DELIMITERS) {
219 validateLineDelimiters(text);
221 fReplaceCmd.replace(fDocument.getLength(), 0, text);
225 * @see IBuffer#close()
227 public void close() {
232 IDocument d= fDocument;
234 d.removePrenotifiedDocumentListener(this);
236 if (fTextFileBuffer != null) {
237 ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
239 manager.disconnect(fTextFileBuffer.getLocation(), new NullProgressMonitor());
240 } catch (CoreException x) {
243 fTextFileBuffer= null;
246 fireBufferChanged(new BufferChangedEvent(this, 0, 0, null));
247 fBufferListeners.clear();
251 * @see IBuffer#getChar(int)
253 public char getChar(int position) {
255 return fDocument.getChar(position);
256 } catch (BadLocationException x) {
257 throw new ArrayIndexOutOfBoundsException();
262 * @see IBuffer#getCharacters()
264 public char[] getCharacters() {
265 String content= getContents();
266 return content == null ? null : content.toCharArray();
270 * @see IBuffer#getContents()
272 public String getContents() {
273 return fDocument.get();
277 * @see IBuffer#getLength()
279 public int getLength() {
280 return fDocument.getLength();
284 * @see IBuffer#getOwner()
286 public IOpenable getOwner() {
291 * @see IBuffer#getText(int, int)
293 public String getText(int offset, int length) {
295 return fDocument.get(offset, length);
296 } catch (BadLocationException x) {
297 throw new ArrayIndexOutOfBoundsException();
302 * @see IBuffer#getUnderlyingResource()
304 public IResource getUnderlyingResource() {
309 * @see IBuffer#hasUnsavedChanges()
311 public boolean hasUnsavedChanges() {
312 return fTextFileBuffer != null ? fTextFileBuffer.isDirty() : false;
316 * @see IBuffer#isClosed()
318 public boolean isClosed() {
319 return fDocument == null;
323 * @see IBuffer#isReadOnly()
325 public boolean isReadOnly() {
326 IResource resource= getUnderlyingResource();
327 return resource == null ? true : resource.getResourceAttributes().isReadOnly();
331 * @see IBuffer#replace(int, int, char[])
333 public void replace(int position, int length, char[] text) {
334 replace(position, length, new String(text));
338 * @see IBuffer#replace(int, int, String)
340 public void replace(int position, int length, String text) {
341 if (DEBUG_LINE_DELIMITERS) {
342 validateLineDelimiters(text);
344 fReplaceCmd.replace(position, length, text);
348 * @see IBuffer#save(IProgressMonitor, boolean)
350 public void save(IProgressMonitor progress, boolean force) throws JavaModelException {
352 if (fTextFileBuffer != null)
353 fTextFileBuffer.commit(progress, force);
354 } catch (CoreException e) {
355 throw new JavaModelException(e);
360 * @see IBuffer#setContents(char[])
362 public void setContents(char[] contents) {
363 setContents(new String(contents));
367 * @see IBuffer#setContents(String)
369 public void setContents(String contents) {
370 int oldLength= fDocument.getLength();
372 if (contents == null) {
375 fSetCmd.set(""); //$NON-NLS-1$
379 // set only if different
380 if (DEBUG_LINE_DELIMITERS) {
381 validateLineDelimiters(contents);
384 if (!contents.equals(fDocument.get()))
385 fSetCmd.set(contents);
390 private void validateLineDelimiters(String contents) {
392 if (fLegalLineDelimiters == null) {
393 // collect all line delimiters in the document
394 HashSet existingDelimiters= new HashSet();
396 for (int i= fDocument.getNumberOfLines() - 1; i >= 0; i-- ) {
398 String curr= fDocument.getLineDelimiter(i);
400 existingDelimiters.add(curr);
402 } catch (BadLocationException e) {
403 PHPeclipsePlugin.log(e);
406 if (existingDelimiters.isEmpty()) {
407 return; // first insertion of a line delimiter: no test
409 fLegalLineDelimiters= existingDelimiters;
413 DefaultLineTracker tracker= new DefaultLineTracker();
414 tracker.set(contents);
416 int lines= tracker.getNumberOfLines();
420 for (int i= 0; i < lines; i++) {
422 String curr= tracker.getLineDelimiter(i);
423 if (curr != null && !fLegalLineDelimiters.contains(curr)) {
424 StringBuffer buf= new StringBuffer("New line delimiter added to new code: "); //$NON-NLS-1$
425 for (int k= 0; k < curr.length(); k++) {
426 buf.append(String.valueOf((int) curr.charAt(k)));
428 PHPeclipsePlugin.log(new Exception(buf.toString()));
430 } catch (BadLocationException e) {
431 PHPeclipsePlugin.log(e);
437 * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
439 public void documentAboutToBeChanged(DocumentEvent event) {
440 // there is nothing to do here
444 * @see IDocumentListener#documentChanged(DocumentEvent)
446 public void documentChanged(DocumentEvent event) {
447 fireBufferChanged(new BufferChangedEvent(this, event.getOffset(), event.getLength(), event.getText()));
450 private void fireBufferChanged(BufferChangedEvent event) {
451 if (fBufferListeners != null && fBufferListeners.size() > 0) {
452 Iterator e= new ArrayList(fBufferListeners).iterator();
454 ((IBufferChangedListener) e.next()).bufferChanged(event);