blob: 0551c809794fdba72d9bbf1f499f62d92e82a7db [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 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.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.LocationKind;
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.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jdt.internal.ui.IResourceLocator;
import org.eclipse.jdt.internal.ui.JavaPlugin;
/**
* A document provider for class files. Class files can be either inside
*/
public class ClassFileDocumentProvider extends FileDocumentProvider {
/**
* An input change listener to request the editor to reread the input.
*/
public interface InputChangeListener {
void inputChanged(IClassFileEditorInput input);
}
/**
* Synchronizes the document with external resource changes.
*/
protected class ClassFileSynchronizer implements IElementChangedListener {
protected IClassFileEditorInput fInput;
protected IPackageFragmentRoot fPackageFragmentRoot;
/**
* Default constructor.
*
* @param input the class file editor input
*/
public ClassFileSynchronizer(IClassFileEditorInput input) {
fInput= input;
IJavaElement parent= fInput.getClassFile().getParent();
while (parent != null && !(parent instanceof IPackageFragmentRoot)) {
parent= parent.getParent();
}
fPackageFragmentRoot= (IPackageFragmentRoot) parent;
}
/**
* Installs the synchronizer.
*/
public void install() {
JavaCore.addElementChangedListener(this);
}
/**
* Uninstalls the synchronizer.
*/
public void uninstall() {
JavaCore.removeElementChangedListener(this);
}
/*
* @see IElementChangedListener#elementChanged
*/
public void elementChanged(ElementChangedEvent e) {
check(fPackageFragmentRoot, e.getDelta());
}
/**
* Recursively check whether the class file has been deleted.
*
* @param input the package fragment root
* @param delta the Java element delta
* @return <code>true</code> if delta processing can be stopped
*/
protected boolean check(IPackageFragmentRoot input, IJavaElementDelta delta) {
IJavaElement element= delta.getElement();
if ((delta.getKind() & IJavaElementDelta.REMOVED) != 0 || (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0) {
// http://dev.eclipse.org/bugs/show_bug.cgi?id=19023
if (element.equals(input.getJavaProject()) || element.equals(input)) {
handleDeleted(fInput);
return true;
}
}
if (((delta.getFlags() & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) && input.equals(element)) {
handleDeleted(fInput);
return true;
}
if (((delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) && input.equals(element)) {
handleDeleted(fInput);
return true;
}
IJavaElementDelta[] subdeltas= delta.getAffectedChildren();
for (int i= 0; i < subdeltas.length; i++) {
if (check(input, subdeltas[i]))
return true;
}
if ((delta.getFlags() & IJavaElementDelta.F_SOURCEDETACHED) != 0 ||
(delta.getFlags() & IJavaElementDelta.F_SOURCEATTACHED) != 0)
{
IClassFile file= fInput != null ? fInput.getClassFile() : null;
IJavaProject project= input != null ? input.getJavaProject() : null;
boolean isOnClasspath= false;
if (file != null && project != null)
isOnClasspath= project.isOnClasspath(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 ClassFileInfo extends FileInfo {
ClassFileSynchronizer fClassFileSynchronizer= null;
ClassFileInfo(IDocument document, IAnnotationModel model, _FileSynchronizer fileSynchronizer) {
super(document, model, fileSynchronizer);
}
ClassFileInfo(IDocument document, IAnnotationModel model, ClassFileSynchronizer classFileSynchronizer) {
super(document, model, null);
fClassFileSynchronizer= classFileSynchronizer;
}
}
/** Input change listeners. */
private List<InputChangeListener> fInputListeners= new ArrayList<InputChangeListener>();
/**
* Creates a new document provider.
*/
public ClassFileDocumentProvider() {
super();
}
/*
* @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput)
*/
@Override
protected boolean setDocumentContent(IDocument document, IEditorInput editorInput, String encoding) throws CoreException {
if (editorInput instanceof IClassFileEditorInput) {
IClassFile classFile= ((IClassFileEditorInput) editorInput).getClassFile();
String source= classFile.getSource();
if (source == null)
source= ""; //$NON-NLS-1$
document.set(source);
return true;
}
return super.setDocumentContent(document, editorInput, encoding);
}
/**
* Creates an annotation model derived from the given class file editor input.
*
* @param classFileEditorInput 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 createClassFileAnnotationModel(IClassFileEditorInput classFileEditorInput) throws CoreException {
IResource resource= null;
IClassFile classFile= classFileEditorInput.getClassFile();
IResourceLocator locator= (IResourceLocator) classFile.getAdapter(IResourceLocator.class);
if (locator != null)
resource= locator.getContainingResource(classFile);
if (resource != null) {
ClassFileMarkerAnnotationModel model= new ClassFileMarkerAnnotationModel(resource);
model.setClassFile(classFile);
return model;
}
return null;
}
/*
* @see org.eclipse.ui.editors.text.StorageDocumentProvider#createEmptyDocument()
* @since 3.1
*/
@Override
protected IDocument createEmptyDocument() {
IDocument document= FileBuffers.getTextFileBufferManager().createEmptyDocument(null, LocationKind.IFILE);
if (document instanceof ISynchronizable)
((ISynchronizable)document).setLockObject(new Object());
return document;
}
/*
* @see AbstractDocumentProvider#createDocument(Object)
*/
@Override
protected IDocument createDocument(Object element) throws CoreException {
IDocument document= super.createDocument(element);
if (document != null) {
JavaTextTools tools= JavaPlugin.getDefault().getJavaTextTools();
tools.setupJavaDocumentPartitioner(document, IJavaPartitions.JAVA_PARTITIONING);
}
return document;
}
/*
* @see AbstractDocumentProvider#createElementInfo(Object)
*/
@Override
protected ElementInfo createElementInfo(Object element) throws CoreException {
if (element instanceof IClassFileEditorInput) {
IClassFileEditorInput input = (IClassFileEditorInput) element;
ExternalClassFileEditorInput external= null;
if (input instanceof ExternalClassFileEditorInput)
external= (ExternalClassFileEditorInput) input;
if (external != null) {
try {
refreshFile(external.getFile());
} catch (CoreException x) {
handleCoreException(x, JavaEditorMessages.ClassFileDocumentProvider_error_createElementInfo);
}
}
IDocument d= createDocument(input);
IAnnotationModel m= createClassFileAnnotationModel(input);
if (external != null) {
ClassFileInfo info= new ClassFileInfo(d, m, (_FileSynchronizer) null);
info.fModificationStamp= computeModificationStamp(external.getFile());
info.fEncoding= getPersistedEncoding(element);
return info;
} else if (input instanceof InternalClassFileEditorInput) {
ClassFileSynchronizer s= new ClassFileSynchronizer(input);
s.install();
ClassFileInfo info= new ClassFileInfo(d, m, s);
info.fEncoding= getPersistedEncoding(element);
return info;
}
}
return null;
}
/*
* @see FileDocumentProvider#disposeElementInfo(Object, ElementInfo)
*/
@Override
protected void disposeElementInfo(Object element, ElementInfo info) {
ClassFileInfo classFileInfo= (ClassFileInfo) info;
if (classFileInfo.fClassFileSynchronizer != null) {
classFileInfo.fClassFileSynchronizer.uninstall();
classFileInfo.fClassFileSynchronizer= null;
}
super.disposeElementInfo(element, info);
}
/*
* @see org.eclipse.ui.texteditor.IDocumentProviderExtension3#isSynchronized(java.lang.Object)
* @since 3.0
*/
@Override
public boolean isSynchronized(Object element) {
Object elementInfo= getElementInfo(element);
if (elementInfo instanceof ClassFileInfo) {
IClassFileEditorInput input= (IClassFileEditorInput)element;
IResource resource;
try {
resource= input.getClassFile().getUnderlyingResource();
} catch (JavaModelException 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(IClassFileEditorInput input) {
if (input == null) {
fireElementDeleted(input);
return;
}
if (input.exists())
return;
IClassFile cf= input.getClassFile();
try {
/*
* Let's try to find the class file - maybe the JAR changed
*/
IType type= cf.getType();
IJavaProject project= cf.getJavaProject();
if (project != null) {
type= project.findType(type.getFullyQualifiedName());
if (type != null) {
IEditorInput editorInput= EditorUtility.getEditorInput(type.getParent());
if (editorInput instanceof IClassFileEditorInput) {
fireInputChanged((IClassFileEditorInput)editorInput);
return;
}
}
}
} catch (JavaModelException x) {
// Don't log and fall through: element deleted
}
fireElementDeleted(input);
}
/**
* Fires input changes to input change listeners.
*
* @param input the class file editor input
*/
protected void fireInputChanged(IClassFileEditorInput input) {
List<InputChangeListener> list= new ArrayList<InputChangeListener>(fInputListeners);
for (Iterator<InputChangeListener> i = list.iterator(); i.hasNext();)
i.next().inputChanged(input);
}
/**
* Adds an input change listener.
*
* @param listener the input change listener
*/
public void addInputChangeListener(InputChangeListener listener) {
fInputListeners.add(listener);
}
/**
* Removes an input change listener.
*
* @param listener the input change listener
*/
public void removeInputChangeListener(InputChangeListener listener) {
fInputListeners.remove(listener);
}
}