| /******************************************************************************* |
| * Copyright (c) 2000, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.jdt.internal.ui.javaeditor; |
| |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBuffer; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourceAttributes; |
| |
| import org.eclipse.swt.widgets.Display; |
| |
| import org.eclipse.jface.text.Assert; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.DefaultLineTracker; |
| import org.eclipse.jface.text.DocumentEvent; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IDocumentListener; |
| import org.eclipse.jface.text.ISynchronizable; |
| |
| import org.eclipse.jdt.core.BufferChangedEvent; |
| import org.eclipse.jdt.core.IBuffer; |
| import org.eclipse.jdt.core.IBufferChangedListener; |
| import org.eclipse.jdt.core.IOpenable; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| import org.eclipse.jdt.ui.JavaUI; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| |
| |
| /** |
| * 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. |
| */ |
| 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 JavaModelException {} |
| public void setContents(char[] contents) {} |
| public void setContents(String contents) {} |
| } |
| |
| |
| /** NULL implementing <code>IBuffer</code> */ |
| public final static IBuffer NULL= new NullBuffer(); |
| |
| |
| /** |
| * Executes a document set content call in the UI thread. |
| */ |
| protected class DocumentSetCommand implements Runnable { |
| |
| private String fContents; |
| |
| public void run() { |
| if (!isClosed()) |
| 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 { |
| if (!isClosed()) |
| 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 static final boolean DEBUG_LINE_DELIMITERS= true; |
| |
| private IOpenable fOwner; |
| private IFile fFile; |
| private ITextFileBuffer fTextFileBuffer; |
| private IDocument fDocument; |
| |
| private DocumentSetCommand fSetCmd= new DocumentSetCommand(); |
| private DocumentReplaceCommand fReplaceCmd= new DocumentReplaceCommand(); |
| |
| private Set fLegalLineDelimiters; |
| |
| private List fBufferListeners= new ArrayList(3); |
| private IStatus fStatus; |
| |
| /* |
| * @since 3.2 |
| */ |
| private IPath fPath; |
| |
| |
| /** |
| * Constructs a new document adapter. |
| * |
| * @since 3.2 |
| */ |
| public DocumentAdapter(IOpenable owner, IPath path) { |
| Assert.isLegal(path != null); |
| |
| fOwner= owner; |
| fPath= path; |
| |
| initialize(); |
| } |
| |
| /** |
| * Constructs a new document adapter. |
| */ |
| public DocumentAdapter(IOpenable owner, IFile file) { |
| |
| fOwner= owner; |
| fFile= file; |
| fPath= fFile.getFullPath(); |
| |
| initialize(); |
| } |
| |
| private void initialize() { |
| ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); |
| try { |
| manager.connect(fPath, new NullProgressMonitor()); |
| fTextFileBuffer= manager.getTextFileBuffer(fPath); |
| fDocument= fTextFileBuffer.getDocument(); |
| } catch (CoreException x) { |
| fStatus= x.getStatus(); |
| fDocument= manager.createEmptyDocument(fPath); |
| if (fDocument instanceof ISynchronizable) |
| ((ISynchronizable)fDocument).setLockObject(new Object()); |
| } |
| fDocument.addPrenotifiedDocumentListener(this); |
| } |
| |
| /** |
| * Returns the status of this document adapter. |
| */ |
| public IStatus getStatus() { |
| if (fStatus != null) |
| return fStatus; |
| if (fTextFileBuffer != null) |
| return fTextFileBuffer.getStatus(); |
| return null; |
| } |
| |
| /** |
| * Returns the adapted document. |
| * |
| * @return the adapted document |
| */ |
| public IDocument getDocument() { |
| return fDocument; |
| } |
| |
| /* |
| * @see IBuffer#addBufferChangedListener(IBufferChangedListener) |
| */ |
| public void addBufferChangedListener(IBufferChangedListener listener) { |
| Assert.isNotNull(listener); |
| if (!fBufferListeners.contains(listener)) |
| fBufferListeners.add(listener); |
| } |
| |
| /* |
| * @see IBuffer#removeBufferChangedListener(IBufferChangedListener) |
| */ |
| public void removeBufferChangedListener(IBufferChangedListener listener) { |
| Assert.isNotNull(listener); |
| fBufferListeners.remove(listener); |
| } |
| |
| /* |
| * @see IBuffer#append(char[]) |
| */ |
| public void append(char[] text) { |
| append(new String(text)); |
| } |
| |
| /* |
| * @see IBuffer#append(String) |
| */ |
| public void append(String text) { |
| if (DEBUG_LINE_DELIMITERS) { |
| validateLineDelimiters(text); |
| } |
| fReplaceCmd.replace(fDocument.getLength(), 0, text); |
| } |
| |
| /* |
| * @see IBuffer#close() |
| */ |
| public void close() { |
| |
| if (isClosed()) |
| return; |
| |
| IDocument d= fDocument; |
| fDocument= null; |
| d.removePrenotifiedDocumentListener(this); |
| |
| if (fTextFileBuffer != null) { |
| ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); |
| try { |
| manager.disconnect(fTextFileBuffer.getLocation(), new NullProgressMonitor()); |
| } catch (CoreException x) { |
| // ignore |
| } |
| fTextFileBuffer= null; |
| } |
| |
| fireBufferChanged(new BufferChangedEvent(this, 0, 0, null)); |
| fBufferListeners.clear(); |
| } |
| |
| /* |
| * @see IBuffer#getChar(int) |
| */ |
| public char getChar(int position) { |
| try { |
| return fDocument.getChar(position); |
| } catch (BadLocationException x) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| } |
| |
| /* |
| * @see IBuffer#getCharacters() |
| */ |
| public char[] getCharacters() { |
| String content= getContents(); |
| return content == null ? null : content.toCharArray(); |
| } |
| |
| /* |
| * @see IBuffer#getContents() |
| */ |
| public String getContents() { |
| return fDocument.get(); |
| } |
| |
| /* |
| * @see IBuffer#getLength() |
| */ |
| public int getLength() { |
| return fDocument.getLength(); |
| } |
| |
| /* |
| * @see IBuffer#getOwner() |
| */ |
| public IOpenable getOwner() { |
| return fOwner; |
| } |
| |
| /* |
| * @see IBuffer#getText(int, int) |
| */ |
| public String getText(int offset, int length) { |
| try { |
| return fDocument.get(offset, length); |
| } catch (BadLocationException x) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| } |
| |
| /* |
| * @see IBuffer#getUnderlyingResource() |
| */ |
| public IResource getUnderlyingResource() { |
| return fFile; |
| } |
| |
| /* |
| * @see IBuffer#hasUnsavedChanges() |
| */ |
| public boolean hasUnsavedChanges() { |
| return fTextFileBuffer != null ? fTextFileBuffer.isDirty() : false; |
| } |
| |
| /* |
| * @see IBuffer#isClosed() |
| */ |
| public boolean isClosed() { |
| return fDocument == null; |
| } |
| |
| /* |
| * @see IBuffer#isReadOnly() |
| */ |
| public boolean isReadOnly() { |
| if (fTextFileBuffer != null) |
| return !fTextFileBuffer.isCommitable(); |
| |
| IResource resource= getUnderlyingResource(); |
| if (resource == null) |
| return true; |
| |
| final ResourceAttributes attributes= resource.getResourceAttributes(); |
| return attributes == null ? false : attributes.isReadOnly(); |
| } |
| |
| /* |
| * @see IBuffer#replace(int, int, char[]) |
| */ |
| public void replace(int position, int length, char[] text) { |
| replace(position, length, new String(text)); |
| } |
| |
| /* |
| * @see IBuffer#replace(int, int, String) |
| */ |
| public void replace(int position, int length, String text) { |
| if (DEBUG_LINE_DELIMITERS) { |
| validateLineDelimiters(text); |
| } |
| fReplaceCmd.replace(position, length, text); |
| } |
| |
| /* |
| * @see IBuffer#save(IProgressMonitor, boolean) |
| */ |
| public void save(IProgressMonitor progress, boolean force) throws JavaModelException { |
| try { |
| if (fTextFileBuffer != null) |
| fTextFileBuffer.commit(progress, force); |
| } catch (CoreException e) { |
| throw new JavaModelException(e); |
| } |
| } |
| |
| /* |
| * @see IBuffer#setContents(char[]) |
| */ |
| public void setContents(char[] contents) { |
| setContents(new String(contents)); |
| } |
| |
| /* |
| * @see IBuffer#setContents(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 |
| if (DEBUG_LINE_DELIMITERS) { |
| validateLineDelimiters(contents); |
| } |
| |
| if (!contents.equals(fDocument.get())) |
| fSetCmd.set(contents); |
| } |
| } |
| |
| |
| private void validateLineDelimiters(String contents) { |
| |
| if (fLegalLineDelimiters == null) { |
| // collect all line delimiters in the document |
| HashSet existingDelimiters= new HashSet(); |
| |
| for (int i= fDocument.getNumberOfLines() - 1; i >= 0; i-- ) { |
| try { |
| String curr= fDocument.getLineDelimiter(i); |
| if (curr != null) { |
| existingDelimiters.add(curr); |
| } |
| } catch (BadLocationException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| if (existingDelimiters.isEmpty()) { |
| return; // first insertion of a line delimiter: no test |
| } |
| fLegalLineDelimiters= existingDelimiters; |
| |
| } |
| |
| DefaultLineTracker tracker= new DefaultLineTracker(); |
| tracker.set(contents); |
| |
| int lines= tracker.getNumberOfLines(); |
| if (lines <= 1) |
| return; |
| |
| for (int i= 0; i < lines; i++) { |
| try { |
| String curr= tracker.getLineDelimiter(i); |
| if (curr != null && !fLegalLineDelimiters.contains(curr)) { |
| StringBuffer buf= new StringBuffer("WARNING: javaeditor.DocumentAdapter added new line delimiter to code: "); //$NON-NLS-1$ |
| for (int k= 0; k < curr.length(); k++) { |
| if (k > 0) |
| buf.append(' '); |
| buf.append((int)curr.charAt(k)); |
| } |
| IStatus status= new Status(IStatus.WARNING, JavaUI.ID_PLUGIN, IStatus.OK, buf.toString(), new Throwable()); |
| JavaPlugin.log(status); |
| } |
| } catch (BadLocationException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| |
| /* |
| * @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); |
| } |
| } |
| } |