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;
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.List;
20 import net.sourceforge.phpdt.core.BufferChangedEvent;
21 import net.sourceforge.phpdt.core.IBuffer;
22 import net.sourceforge.phpdt.core.IBufferChangedListener;
23 import net.sourceforge.phpdt.core.IOpenable;
24 import net.sourceforge.phpdt.core.JavaModelException;
25 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
27 import org.eclipse.core.filebuffers.FileBuffers;
28 import org.eclipse.core.filebuffers.ITextFileBuffer;
29 import org.eclipse.core.filebuffers.ITextFileBufferManager;
30 import org.eclipse.core.resources.IFile;
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.runtime.CoreException;
33 import org.eclipse.core.runtime.IPath;
34 import org.eclipse.core.runtime.IProgressMonitor;
35 import org.eclipse.core.runtime.IStatus;
36 import org.eclipse.core.runtime.NullProgressMonitor;
37 import org.eclipse.jface.text.Assert;
38 import org.eclipse.jface.text.BadLocationException;
39 import org.eclipse.jface.text.DefaultLineTracker;
40 import org.eclipse.jface.text.DocumentEvent;
41 import org.eclipse.jface.text.IDocument;
42 import org.eclipse.jface.text.IDocumentListener;
43 import org.eclipse.swt.widgets.Display;
46 * Adapts <code>IDocument</code> to <code>IBuffer</code>. Uses the same
47 * algorithm as the text widget to determine the buffer's line delimiter. All
48 * text inserted into the buffer is converted to this line delimiter. This class
49 * is <code>public</code> for test purposes only.
51 public class DocumentAdapter implements IBuffer, IDocumentListener {
54 * Internal implementation of a NULL instanceof IBuffer.
56 static private class NullBuffer implements IBuffer {
57 public void addBufferChangedListener(IBufferChangedListener listener) {
60 public void append(char[] text) {
63 public void append(String text) {
69 public char getChar(int position) {
73 public char[] getCharacters() {
77 public String getContents() {
81 public int getLength() {
85 public IOpenable getOwner() {
89 public String getText(int offset, int length) {
93 public IResource getUnderlyingResource() {
97 public boolean hasUnsavedChanges() {
101 public boolean isClosed() {
105 public boolean isReadOnly() {
109 public void removeBufferChangedListener(IBufferChangedListener listener) {
112 public void replace(int position, int length, char[] text) {
115 public void replace(int position, int length, String text) {
118 public void save(IProgressMonitor progress, boolean force)
119 throws JavaModelException {
122 public void setContents(char[] contents) {
125 public void setContents(String contents) {
129 /** NULL implementing <code>IBuffer</code> */
130 public final static IBuffer NULL = new NullBuffer();
133 * Executes a document set content call in the ui thread.
135 protected class DocumentSetCommand implements Runnable {
137 private String fContents;
140 fDocument.set(fContents);
143 public void set(String contents) {
144 fContents = contents;
145 Display.getDefault().syncExec(this);
150 * Executes a document replace call in the ui thread.
152 protected class DocumentReplaceCommand implements Runnable {
158 private String fText;
162 fDocument.replace(fOffset, fLength, fText);
163 } catch (BadLocationException x) {
168 public void replace(int offset, int length, String text) {
172 Display.getDefault().syncExec(this);
176 private static final boolean DEBUG_LINE_DELIMITERS = true;
178 private IOpenable fOwner;
182 private ITextFileBuffer fTextFileBuffer;
184 private IDocument fDocument;
186 private DocumentSetCommand fSetCmd = new DocumentSetCommand();
188 private DocumentReplaceCommand fReplaceCmd = new DocumentReplaceCommand();
190 private Set fLegalLineDelimiters;
192 private List fBufferListeners = new ArrayList(3);
194 private IStatus fStatus;
197 * This method is <code>public</code> for test purposes only.
199 public DocumentAdapter(IOpenable owner, IFile file) {
207 private void initialize() {
208 ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager();
209 IPath location = fFile.getFullPath();
211 manager.connect(location, new NullProgressMonitor());
212 fTextFileBuffer = manager.getTextFileBuffer(location);
213 fDocument = fTextFileBuffer.getDocument();
214 } catch (CoreException x) {
215 fStatus = x.getStatus();
216 fDocument = manager.createEmptyDocument(location);
218 fDocument.addPrenotifiedDocumentListener(this);
222 * Returns the status of this document adapter.
224 public IStatus getStatus() {
227 if (fTextFileBuffer != null)
228 return fTextFileBuffer.getStatus();
233 * Returns the adapted document.
235 * @return the adapted document
237 public IDocument getDocument() {
242 * @see IBuffer#addBufferChangedListener(IBufferChangedListener)
244 public void addBufferChangedListener(IBufferChangedListener listener) {
245 Assert.isNotNull(listener);
246 if (!fBufferListeners.contains(listener))
247 fBufferListeners.add(listener);
251 * @see IBuffer#removeBufferChangedListener(IBufferChangedListener)
253 public void removeBufferChangedListener(IBufferChangedListener listener) {
254 Assert.isNotNull(listener);
255 fBufferListeners.remove(listener);
259 * @see IBuffer#append(char[])
261 public void append(char[] text) {
262 append(new String(text));
266 * @see IBuffer#append(String)
268 public void append(String text) {
269 if (DEBUG_LINE_DELIMITERS) {
270 validateLineDelimiters(text);
272 fReplaceCmd.replace(fDocument.getLength(), 0, text);
276 * @see IBuffer#close()
278 public void close() {
283 IDocument d = fDocument;
285 d.removePrenotifiedDocumentListener(this);
287 if (fTextFileBuffer != null) {
288 ITextFileBufferManager manager = FileBuffers
289 .getTextFileBufferManager();
291 manager.disconnect(fTextFileBuffer.getLocation(),
292 new NullProgressMonitor());
293 } catch (CoreException x) {
296 fTextFileBuffer = null;
299 fireBufferChanged(new BufferChangedEvent(this, 0, 0, null));
300 fBufferListeners.clear();
304 * @see IBuffer#getChar(int)
306 public char getChar(int position) {
308 return fDocument.getChar(position);
309 } catch (BadLocationException x) {
310 throw new ArrayIndexOutOfBoundsException();
315 * @see IBuffer#getCharacters()
317 public char[] getCharacters() {
318 String content = getContents();
319 return content == null ? null : content.toCharArray();
323 * @see IBuffer#getContents()
325 public String getContents() {
326 return fDocument.get();
330 * @see IBuffer#getLength()
332 public int getLength() {
333 return fDocument.getLength();
337 * @see IBuffer#getOwner()
339 public IOpenable getOwner() {
344 * @see IBuffer#getText(int, int)
346 public String getText(int offset, int length) {
348 return fDocument.get(offset, length);
349 } catch (BadLocationException x) {
350 throw new ArrayIndexOutOfBoundsException();
355 * @see IBuffer#getUnderlyingResource()
357 public IResource getUnderlyingResource() {
362 * @see IBuffer#hasUnsavedChanges()
364 public boolean hasUnsavedChanges() {
365 return fTextFileBuffer != null ? fTextFileBuffer.isDirty() : false;
369 * @see IBuffer#isClosed()
371 public boolean isClosed() {
372 return fDocument == null;
376 * @see IBuffer#isReadOnly()
378 public boolean isReadOnly() {
379 IResource resource = getUnderlyingResource();
380 return resource == null ? true : resource.getResourceAttributes()
385 * @see IBuffer#replace(int, int, char[])
387 public void replace(int position, int length, char[] text) {
388 replace(position, length, new String(text));
392 * @see IBuffer#replace(int, int, String)
394 public void replace(int position, int length, String text) {
395 if (DEBUG_LINE_DELIMITERS) {
396 validateLineDelimiters(text);
398 fReplaceCmd.replace(position, length, text);
402 * @see IBuffer#save(IProgressMonitor, boolean)
404 public void save(IProgressMonitor progress, boolean force)
405 throws JavaModelException {
407 if (fTextFileBuffer != null)
408 fTextFileBuffer.commit(progress, force);
409 } catch (CoreException e) {
410 throw new JavaModelException(e);
415 * @see IBuffer#setContents(char[])
417 public void setContents(char[] contents) {
418 setContents(new String(contents));
422 * @see IBuffer#setContents(String)
424 public void setContents(String contents) {
425 int oldLength = fDocument.getLength();
427 if (contents == null) {
430 fSetCmd.set(""); //$NON-NLS-1$
434 // set only if different
435 if (DEBUG_LINE_DELIMITERS) {
436 validateLineDelimiters(contents);
439 if (!contents.equals(fDocument.get()))
440 fSetCmd.set(contents);
444 private void validateLineDelimiters(String contents) {
446 if (fLegalLineDelimiters == null) {
447 // collect all line delimiters in the document
448 HashSet existingDelimiters = new HashSet();
450 for (int i = fDocument.getNumberOfLines() - 1; i >= 0; i--) {
452 String curr = fDocument.getLineDelimiter(i);
454 existingDelimiters.add(curr);
456 } catch (BadLocationException e) {
457 PHPeclipsePlugin.log(e);
460 if (existingDelimiters.isEmpty()) {
461 return; // first insertion of a line delimiter: no test
463 fLegalLineDelimiters = existingDelimiters;
467 DefaultLineTracker tracker = new DefaultLineTracker();
468 tracker.set(contents);
470 int lines = tracker.getNumberOfLines();
474 for (int i = 0; i < lines; i++) {
476 String curr = tracker.getLineDelimiter(i);
477 if (curr != null && !fLegalLineDelimiters.contains(curr)) {
478 StringBuffer buf = new StringBuffer(
479 "New line delimiter added to new code: "); //$NON-NLS-1$
480 for (int k = 0; k < curr.length(); k++) {
481 buf.append(String.valueOf((int) curr.charAt(k)));
483 PHPeclipsePlugin.log(new Exception(buf.toString()));
485 } catch (BadLocationException e) {
486 PHPeclipsePlugin.log(e);
492 * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
494 public void documentAboutToBeChanged(DocumentEvent event) {
495 // there is nothing to do here
499 * @see IDocumentListener#documentChanged(DocumentEvent)
501 public void documentChanged(DocumentEvent event) {
502 fireBufferChanged(new BufferChangedEvent(this, event.getOffset(), event
503 .getLength(), event.getText()));
506 private void fireBufferChanged(BufferChangedEvent event) {
507 if (fBufferListeners != null && fBufferListeners.size() > 0) {
508 Iterator e = new ArrayList(fBufferListeners).iterator();
510 ((IBufferChangedListener) e.next()).bufferChanged(event);