package org.rubypeople.rdt.internal.ui.rubyeditor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.editors.text.FileDocumentProvider; import org.rubypeople.rdt.core.ElementChangedEvent; import org.rubypeople.rdt.core.IElementChangedListener; import org.rubypeople.rdt.core.IRubyElement; import org.rubypeople.rdt.core.IRubyElementDelta; import org.rubypeople.rdt.core.IRubyProject; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.ISourceFolderRoot; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.internal.ui.RubyPlugin; import org.rubypeople.rdt.internal.ui.text.IRubyPartitions; import org.rubypeople.rdt.ui.text.RubyTextTools; public class RubyScriptDocumentProvider extends FileDocumentProvider { /** * An input change listener to request the editor to reread the input. */ public interface InputChangeListener { void inputChanged(IRubyScriptEditorInput input); } /** * Synchronizes the document with external resource changes. */ protected class RubyScriptSynchronizer implements IElementChangedListener { protected IRubyScriptEditorInput fInput; protected ISourceFolderRoot fSourceFolderRoot; /** * Default constructor. */ public RubyScriptSynchronizer(IRubyScriptEditorInput input) { fInput= input; IRubyElement parent= fInput.getRubyScript().getParent(); while (parent != null && !(parent instanceof ISourceFolderRoot)) { parent= parent.getParent(); } fSourceFolderRoot= (ISourceFolderRoot) parent; } /** * Installs the synchronizer. */ public void install() { RubyCore.addElementChangedListener(this); } /** * Uninstalls the synchronizer. */ public void uninstall() { RubyCore.removeElementChangedListener(this); } /* * @see IElementChangedListener#elementChanged */ public void elementChanged(ElementChangedEvent e) { check(fSourceFolderRoot, e.getDelta()); } /** * Recursively check whether the class file has been deleted. * Returns true if delta processing can be stopped. */ protected boolean check(ISourceFolderRoot input, IRubyElementDelta delta) { IRubyElement element= delta.getElement(); if ((delta.getKind() & IRubyElementDelta.REMOVED) != 0 || (delta.getFlags() & IRubyElementDelta.F_CLOSED) != 0) { // http://dev.eclipse.org/bugs/show_bug.cgi?id=19023 if (element.equals(input.getRubyProject()) || element.equals(input)) { handleDeleted(fInput); return true; } } if (((delta.getFlags() & IRubyElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) && input.equals(element)) { handleDeleted(fInput); return true; } if (((delta.getFlags() & IRubyElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) && input.equals(element)) { handleDeleted(fInput); return true; } IRubyElementDelta[] subdeltas= delta.getAffectedChildren(); for (int i= 0; i < subdeltas.length; i++) { if (check(input, subdeltas[i])) return true; } if ((delta.getFlags() & IRubyElementDelta.F_SOURCEDETACHED) != 0 || (delta.getFlags() & IRubyElementDelta.F_SOURCEATTACHED) != 0) { IRubyScript file= fInput != null ? fInput.getRubyScript() : null; IRubyProject project= input != null ? input.getRubyProject() : null; boolean isOnClasspath= false; if (file != null && project != null) isOnClasspath= project.isOnLoadpath(file); if (isOnClasspath) { fireInputChanged(fInput); return false; } else { handleDeleted(fInput); return true; } } return false; } } /** * Correcting the visibility of <code>FileSynchronizer</code>. */ protected class _FileSynchronizer extends FileSynchronizer { public _FileSynchronizer(IFileEditorInput fileEditorInput) { super(fileEditorInput); } } /** * Bundle of all required informations. */ protected class RubyScriptInfo extends FileInfo { RubyScriptSynchronizer fRubyScriptSynchronizer= null; RubyScriptInfo(IDocument document, IAnnotationModel model, _FileSynchronizer fileSynchronizer) { super(document, model, fileSynchronizer); } RubyScriptInfo(IDocument document, IAnnotationModel model, RubyScriptSynchronizer classFileSynchronizer) { super(document, model, null); fRubyScriptSynchronizer= classFileSynchronizer; } } /** Input change listeners. */ private List fInputListeners= new ArrayList(); /** * Creates a new document provider. */ public RubyScriptDocumentProvider() { super(); } /* * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput) */ protected boolean setDocumentContent(IDocument document, IEditorInput editorInput, String encoding) throws CoreException { if (editorInput instanceof IRubyScriptEditorInput) { IRubyScript rubyScript= ((IRubyScriptEditorInput) editorInput).getRubyScript(); document.set(rubyScript.getSource()); return true; } return super.setDocumentContent(document, editorInput, encoding); } /** * Creates an annotation model derived from the given class file editor input. * * @param rubyScriptEditorInput the editor input from which to query the annotations * @return the created annotation model * @exception CoreException if the editor input could not be accessed */ protected IAnnotationModel createRubyScriptAnnotationModel(IRubyScriptEditorInput rubyScriptEditorInput) throws CoreException { IRubyScript script = rubyScriptEditorInput.getRubyScript(); ExternalFileRubyAnnotationModel model = new ExternalFileRubyAnnotationModel(script); return model; } /* * @see org.eclipse.ui.editors.text.StorageDocumentProvider#createEmptyDocument() * @since 3.1 */ protected IDocument createEmptyDocument() { IDocument document= FileBuffers.getTextFileBufferManager().createEmptyDocument(null); if (document instanceof ISynchronizable) ((ISynchronizable)document).setLockObject(new Object()); return document; } /* * @see AbstractDocumentProvider#createDocument(Object) */ protected IDocument createDocument(Object element) throws CoreException { IDocument document= super.createDocument(element); if (document != null) { RubyTextTools tools= RubyPlugin.getDefault().getRubyTextTools(); tools.setupRubyDocumentPartitioner(document, IRubyPartitions.RUBY_PARTITIONING); } return document; } /* * @see AbstractDocumentProvider#createElementInfo(Object) */ protected ElementInfo createElementInfo(Object element) throws CoreException { if (element instanceof IRubyScriptEditorInput) { IRubyScriptEditorInput input = (IRubyScriptEditorInput) element; // ExternalRubyScriptEditorInput external= null; // if (input instanceof ExternalRubyScriptEditorInput) // external= (ExternalRubyScriptEditorInput) input; // // if (external != null) { // try { // refreshFile(external.getFile()); // } catch (CoreException x) { // handleCoreException(x, JavaEditorMessages.RubyScriptDocumentProvider_error_createElementInfo); // } // } IDocument d= createDocument(input); IAnnotationModel m= createRubyScriptAnnotationModel(input); // if (external != null) { // RubyScriptInfo info= new RubyScriptInfo(d, m, (_FileSynchronizer) null); // info.fModificationStamp= computeModificationStamp(external.getFile()); // info.fEncoding= getPersistedEncoding(element); // return info; // } else if (input instanceof RubyScriptEditorInput) { RubyScriptSynchronizer s= new RubyScriptSynchronizer(input); s.install(); RubyScriptInfo info= new RubyScriptInfo(d, m, s); info.fEncoding= getPersistedEncoding(element); return info; } } return null; } /* * @see FileDocumentProvider#disposeElementInfo(Object, ElementInfo) */ protected void disposeElementInfo(Object element, ElementInfo info) { RubyScriptInfo classFileInfo= (RubyScriptInfo) info; if (classFileInfo.fRubyScriptSynchronizer != null) { classFileInfo.fRubyScriptSynchronizer.uninstall(); classFileInfo.fRubyScriptSynchronizer= null; } super.disposeElementInfo(element, info); } /* * @see AbstractDocumentProvider#doSaveDocument(IProgressMonitor, Object, IDocument) */ protected void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document) throws CoreException { } /* * @see org.eclipse.ui.texteditor.IDocumentProviderExtension3#isSynchronized(java.lang.Object) * @since 3.0 */ public boolean isSynchronized(Object element) { Object elementInfo= getElementInfo(element); if (elementInfo instanceof RubyScriptInfo) { IRubyScriptEditorInput input= (IRubyScriptEditorInput)element; IResource resource; try { resource= input.getRubyScript().getUnderlyingResource(); } catch (RubyModelException e) { return true; } return resource == null || resource.isSynchronized(IResource.DEPTH_ZERO); } return false; } /** * Handles the deletion of the element underlying the given class file editor input. * @param input the editor input */ protected void handleDeleted(IRubyScriptEditorInput input) { fireElementDeleted(input); } /** * Fires input changes to input change listeners. */ protected void fireInputChanged(IRubyScriptEditorInput input) { List list= new ArrayList(fInputListeners); for (Iterator i = list.iterator(); i.hasNext();) ((InputChangeListener) i.next()).inputChanged(input); } /** * Adds an input change listener. */ public void addInputChangeListener(InputChangeListener listener) { fInputListeners.add(listener); } /** * Removes an input change listener. */ public void removeInputChangeListener(InputChangeListener listener) { fInputListeners.remove(listener); } }