package org.eclipse.jdt.internal.core; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import org.eclipse.core.runtime.*; | |
import org.eclipse.core.resources.*; | |
import org.eclipse.core.resources.*; | |
import org.eclipse.jdt.internal.compiler.*; | |
import org.eclipse.jdt.internal.compiler.env.INameEnvironment; | |
import org.eclipse.jdt.core.*; | |
import org.eclipse.jdt.internal.core.builder.IDevelopmentContext; | |
import org.eclipse.jdt.internal.core.builder.IPackage; | |
import org.eclipse.jdt.internal.core.builder.IState; | |
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; | |
import org.eclipse.jdt.internal.core.builder.impl.*; | |
import org.eclipse.jdt.internal.core.search.indexing.*; | |
import java.io.*; | |
import java.util.*; | |
import java.util.zip.ZipFile; | |
import javax.xml.parsers.*; | |
import org.apache.xerces.dom.*; | |
import org.apache.xml.serialize.*; | |
import org.w3c.dom.*; | |
import org.xml.sax.*; | |
import org.eclipse.jdt.internal.core.builder.NotPresentException; | |
/** | |
* The <code>JavaModelManager</code> manages instances of <code>IJavaModel</code>. | |
* <code>IElementChangedListener</code>s register with the <code>JavaModelManager</code>, | |
* and receive <code>ElementChangedEvent</code>s for all <code>IJavaModel</code>s. | |
* <p> | |
* The single instance of <code>JavaModelManager</code> is available from | |
* the static method <code>JavaModelManager.getJavaModelManager()</code>. | |
*/ | |
public class JavaModelManager implements IResourceChangeListener, ISaveParticipant { | |
/** | |
* The singleton manager | |
*/ | |
protected static JavaModelManager fgManager= null; | |
/** | |
* Active Java Model Info | |
*/ | |
protected JavaModelInfo fModelInfo= null; | |
/** | |
* Turns delta firing on/off. By default it is on. | |
*/ | |
protected boolean fFire= true; | |
/** | |
* Queue of deltas created explicily by the Java Model that | |
* have yet to be fired. | |
*/ | |
protected Vector fJavaModelDeltas= new Vector(); | |
/** | |
* Queue of deltas created as translations of ResourceDeltas that | |
* have yet to be fired. | |
*/ | |
protected Vector fResourceDeltas= new Vector(); | |
/** | |
* Collection of listeners for Java element deltas | |
*/ | |
protected Vector fElementChangedListeners= new Vector(); | |
/** | |
* Collection of projects that are in the process of being deleted. | |
* Project reside in this cache from the time the plugin receives | |
* the #deleting message until they resource delta is received | |
* claiming the project has been deleted. The java model will not allow | |
* a project that is being deleted to be opened - since this can leave | |
* files open, causing the delete to fail. | |
* | |
* fix for 1FW67PA | |
*/ | |
protected Vector fProjectsBeingDeleted= new Vector(); | |
/** | |
* Used to convert <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s. | |
*/ | |
protected DeltaProcessor fDeltaProcessor= new DeltaProcessor(); | |
public static boolean ENABLE_INDEXING= true; | |
/** | |
* Local Java workspace properties file name (generated inside JavaCore plugin state location) | |
*/ | |
private static final String WKS_PROP_FILENAME= "workspace.properties"/*nonNLS*/; | |
/** | |
* Name of the handle id attribute in a Java marker | |
*/ | |
private static final String ATT_HANDLE_ID= "org.eclipse.jdt.internal.core.JavaModelManager.handleId"/*nonNLS*/; | |
/** | |
* Table from IProject to PerProjectInfo. | |
*/ | |
protected Hashtable perProjectInfo = new Hashtable(5); | |
static class PerProjectInfo { | |
IProject project; | |
IDevelopmentContext developmentContext = new JavaDevelopmentContextImpl(); | |
byte[] savedStateFingerprint; | |
boolean triedRead = false; | |
PerProjectInfo(IProject project) { | |
this.project = project; | |
} | |
IState getLastBuiltState() { | |
try { | |
return developmentContext.getCurrentState(); | |
} catch (NotPresentException e) { | |
return null; | |
} | |
} | |
void setLastBuiltState(IState state) { | |
developmentContext.setCurrentState(state); | |
} | |
}; | |
public final static boolean VERBOSE = false; | |
/** | |
* Line separator to use throughout the JavaModel for any source edit operation | |
*/ | |
public static String LINE_SEPARATOR = System.getProperty("line.separator"/*nonNLS*/); | |
/** | |
* Constructs a new JavaModelManager | |
*/ | |
private JavaModelManager() { | |
} | |
/** | |
* addElementChangedListener method comment. | |
*/ | |
public void addElementChangedListener(IElementChangedListener listener) { | |
if (fElementChangedListeners.indexOf(listener) < 0) { | |
fElementChangedListeners.addElement(listener); | |
} | |
} | |
/* | |
* Checks that the delta contains an added project. In this case, | |
* removes it from the list of projects being deleted. | |
*/ | |
public void checkProjectBeingAdded(IResourceDelta delta) { | |
IResource resource = delta.getResource(); | |
switch (resource.getType()) { | |
case IResource.ROOT : | |
IResourceDelta[] children = delta.getAffectedChildren(); | |
for (int i = 0, length = children.length; i < length; i++) { | |
this.checkProjectBeingAdded(children[i]); | |
} | |
case IResource.PROJECT : | |
if (delta.getKind() == IResourceDelta.ADDED) { | |
fProjectsBeingDeleted.remove(delta.getResource()); | |
} | |
} | |
} | |
/** | |
* Used when incrementally building, so as to force a partial refresh of the Java Model before it got a | |
* chance to update by itself. | |
*/ | |
public void closeAffectedElements(IResourceDelta delta) { | |
fDeltaProcessor.closeAffectedElements(delta); | |
} | |
/** | |
* Convert options Map into ConfigurableOption understood by the infrastructure | |
* | |
* Should be revisited | |
*/ | |
public static ConfigurableOption[] convertConfigurableOptions(Hashtable optionMap) { | |
Enumeration optionNames = optionMap.keys(); | |
CompilerOptions compilerOptions = new CompilerOptions(); | |
int index = 0; | |
while (optionNames.hasMoreElements()){ | |
String optionName = (String)optionNames.nextElement(); | |
String optionValue = (String)optionMap.get(optionName); | |
if (optionName.equals(JavaCore.COMPILER_LOCAL_VARIABLE_ATTR)){ | |
if (optionValue.equals(JavaCore.GENERATE)){ | |
compilerOptions.produceDebugAttributes( | |
compilerOptions.getDebugAttributesMask() | CompilerOptions.Vars); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.DO_NOT_GENERATE)){ | |
compilerOptions.produceDebugAttributes( | |
compilerOptions.getDebugAttributesMask() & ~CompilerOptions.Vars); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_LINE_NUMBER_ATTR)){ | |
if (optionValue.equals(JavaCore.GENERATE)){ | |
compilerOptions.produceDebugAttributes( | |
compilerOptions.getDebugAttributesMask() | CompilerOptions.Lines); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.DO_NOT_GENERATE)){ | |
compilerOptions.produceDebugAttributes( | |
compilerOptions.getDebugAttributesMask() & ~CompilerOptions.Lines); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_SOURCE_FILE_ATTR)){ | |
if (optionValue.equals(JavaCore.GENERATE)){ | |
compilerOptions.produceDebugAttributes( | |
compilerOptions.getDebugAttributesMask() | CompilerOptions.Source); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.DO_NOT_GENERATE)){ | |
compilerOptions.produceDebugAttributes( | |
compilerOptions.getDebugAttributesMask() & ~CompilerOptions.Source); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_CODEGEN_UNUSED_LOCAL)){ | |
if (optionValue.equals(JavaCore.PRESERVE)){ | |
compilerOptions.preserveAllLocalVariables(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.OPTIMIZE_OUT)){ | |
compilerOptions.preserveAllLocalVariables(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)){ | |
if (optionValue.equals(JavaCore.VERSION_1_1)){ | |
compilerOptions.setTargetJDK(CompilerOptions.JDK1_1); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.VERSION_1_2)){ | |
compilerOptions.setTargetJDK(CompilerOptions.JDK1_2); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_UNREACHABLE_CODE)){ | |
if (optionValue.equals(JavaCore.ERROR)){ | |
compilerOptions.handleUnreachableCodeAsError(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleUnreachableCodeAsError(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_INVALID_IMPORT)){ | |
if (optionValue.equals(JavaCore.ERROR)){ | |
compilerOptions.handleImportProblemAsError(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleImportProblemAsError(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_OVERRIDING_PACKAGE_DEFAULT_METHOD)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleOverriddenPackageDefaultMethodAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleOverriddenPackageDefaultMethodAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_METHOD_WITH_CONSTRUCTOR_NAME)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleMethodWithConstructorNameAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleMethodWithConstructorNameAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_DEPRECATION)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleDeprecationUseAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleDeprecationUseAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_HIDDEN_CATCH_BLOCK)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleMaskedCatchBlockAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleMaskedCatchBlockAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_UNUSED_LOCAL)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleUnusedLocalVariableAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleUnusedLocalVariableAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_UNUSED_PARAMETER)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleUnusedArgumentAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleUnusedArgumentAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_SYNTHETIC_ACCESS_EMULATION)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleAccessEmulationAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleAccessEmulationAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
if (optionName.equals(JavaCore.COMPILER_PB_NON_EXTERNALIZED_STRING_LITERAL)){ | |
if (optionValue.equals(JavaCore.WARNING)){ | |
compilerOptions.handleNonExternalizedStringLiteralAsWarning(true); | |
continue; | |
} | |
if (optionValue.equals(JavaCore.IGNORE)){ | |
compilerOptions.handleNonExternalizedStringLiteralAsWarning(false); | |
continue; | |
} | |
continue; | |
} | |
} | |
return compilerOptions.getConfigurableOptions(Locale.getDefault()); | |
} | |
/** | |
* Note that the project is now deleted. | |
* | |
* fix for 1FW67PA | |
*/ | |
public void deleted(IProject project) { | |
fProjectsBeingDeleted.removeElement(project); | |
} | |
/** | |
* Note that the project is about to be deleted. | |
* | |
* fix for 1FW67PA | |
*/ | |
public void deleting(IProject project) { | |
IndexManager indexManager= getIndexManager(); | |
if (indexManager != null) indexManager.deleting(project); | |
if (!fProjectsBeingDeleted.contains(project)) { | |
fProjectsBeingDeleted.addElement(project); | |
} | |
} | |
/** | |
* @see ISaveParticipant | |
*/ | |
public void doneSaving(ISaveContext context){ | |
} | |
/** | |
* Make sure the resource content is available locally | |
*/ | |
public void ensureLocal(IResource resource) throws CoreException { | |
// need to be tuned once having VCM support | |
// resource.ensureLocal(IResource.DEPTH_ZERO, null); | |
if (!resource.isLocal(IResource.DEPTH_ZERO) || !resource.exists()) { // project is always local but might not exist | |
throw new CoreException(new JavaModelStatus(IJavaModelStatusConstants.NO_LOCAL_CONTENTS, resource.getFullPath())); | |
} | |
} | |
/** | |
* Merges resource deltas and Java Model deltas, and fires the | |
* result, flushing all deltas. If the firing mode has been | |
* turned off, this has no effect. | |
*/ | |
public void fire() { | |
if (fFire) { | |
Enumeration deltas= null; | |
if (fJavaModelDeltas.isEmpty()) { | |
deltas= fResourceDeltas.elements(); | |
} else { | |
deltas= fJavaModelDeltas.elements(); | |
} | |
try { | |
while (deltas.hasMoreElements()) { | |
IJavaElementDelta delta= (IJavaElementDelta) deltas.nextElement(); | |
ElementChangedEvent event= new ElementChangedEvent(delta); | |
// Clone the listeners since they could remove themselves when told about the event | |
// (eg. a type hierarchy becomes invalid (and thus it removes itself) when the type is removed | |
Vector listeners= (Vector) fElementChangedListeners.clone(); | |
for (int i= 0; i < listeners.size(); i++) { | |
IElementChangedListener listener= (IElementChangedListener) listeners.elementAt(i); | |
listener.elementChanged(event); | |
} | |
} | |
} finally { | |
// empty the queues | |
flush(); | |
} | |
} | |
} | |
/** | |
* Flushes all deltas without firing them. | |
*/ | |
protected void flush() { | |
fJavaModelDeltas= new Vector(); | |
fResourceDeltas= new Vector(); | |
} | |
/** | |
* Returns the development context to use for the given project. | |
* | |
* @private for use by image builder only | |
*/ | |
public IDevelopmentContext getDevelopmentContext(IProject project) { | |
return getPerProjectInfo(project).developmentContext; | |
} | |
/** | |
* Returns the set of elements which are out of synch with their buffers. | |
*/ | |
protected Hashtable getElementsOutOfSynchWithBuffers() { | |
if (fModelInfo == null) { | |
return new Hashtable(1); | |
} else { | |
return fModelInfo.fElementsOutOfSynchWithBuffers; | |
} | |
} | |
/** | |
* Returns the <code>IJavaElement</code> represented by the <code>String</code> | |
* memento. | |
* @see getHandleMemento() | |
*/ | |
public IJavaElement getHandleFromMemento(String memento) throws JavaModelException { | |
if (memento == null) { | |
return null; | |
} | |
IWorkspace workspace= ResourcesPlugin.getWorkspace(); | |
if (workspace == null) { | |
return null; | |
} | |
JavaModel model= (JavaModel) getJavaModel(workspace); | |
if (memento.equals(""/*nonNLS*/)){ // workspace memento | |
return model; | |
} | |
int modelEnd= memento.indexOf(JavaElement.JEM_JAVAPROJECT); | |
if (modelEnd == -1) { | |
return null; | |
} | |
boolean returnProject= false; | |
int projectEnd= memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENTROOT, modelEnd); | |
if (projectEnd == -1) { | |
projectEnd= memento.length(); | |
returnProject= true; | |
} | |
String projectName= memento.substring(modelEnd + 1, projectEnd); | |
JavaProject proj= (JavaProject) model.getJavaProject(projectName); | |
if (returnProject) { | |
return proj; | |
} | |
int rootEnd= memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENT, projectEnd + 1); | |
if (rootEnd == -1) { | |
return proj.getPackageFragmentRoot(new Path(Path.SEPARATOR + memento.substring(modelEnd + 1))); | |
} | |
String rootName= null; | |
if (rootEnd == projectEnd - 1) { | |
//default root | |
rootName= IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH; | |
} else { | |
rootName= memento.substring(projectEnd + 1, rootEnd); | |
} | |
IPath rootPath= new Path(rootName); | |
IPackageFragmentRoot root= null; | |
if (rootPath.isAbsolute()) { | |
root= proj.getPackageFragmentRoot(rootName); | |
} else { | |
root= proj.getPackageFragmentRoot(proj.getProject().getFullPath().append(rootName)); | |
} | |
if (root == null) | |
return null; | |
int end= memento.indexOf(JavaElement.JEM_COMPILATIONUNIT, rootEnd); | |
if (end == -1) { | |
end= memento.indexOf(JavaElement.JEM_CLASSFILE, rootEnd); | |
if (end == -1) { | |
if (rootEnd + 1 == memento.length()) { | |
return root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME); | |
} else { | |
return root.getPackageFragment(memento.substring(rootEnd + 1)); | |
} | |
} | |
//deal with class file and binary members | |
return model.getHandleFromMementoForBinaryMembers(memento, root, rootEnd, end); | |
} | |
//deal with compilation units and source members | |
return model.getHandleFromMementoForSourceMembers(memento, root, rootEnd, end); | |
} | |
public IndexManager getIndexManager() { | |
return fDeltaProcessor.indexManager; | |
} | |
/** | |
* Returns the info for the element. | |
*/ | |
protected Object getInfo(IJavaElement element) { | |
if (fModelInfo == null) { | |
return null; | |
} | |
int elementType= ((JavaElement) element).fLEType; | |
if (elementType == IJavaElement.JAVA_MODEL) { | |
return fModelInfo; | |
} | |
if (((JavaElement) element).fLEType <= IJavaElement.CLASS_FILE) { | |
return fModelInfo.fLRUCache.get(element); | |
} else { | |
return fModelInfo.fChildrenCache.get(element); | |
} | |
} | |
/** | |
* Returns the handle to the active Java Model, or <code>null</code> if there | |
* is no active Java Model. | |
*/ | |
public IJavaModel getJavaModel() { | |
if (fModelInfo == null) { | |
return null; | |
} else { | |
return fModelInfo.getJavaModel(); | |
} | |
} | |
/** | |
* Returns the JavaModel for the given workspace, creating | |
* it if it does not yet exist. | |
*/ | |
public static JavaModel getJavaModel(IWorkspace workspace) { | |
JavaModelInfo modelInfo= getJavaModelManager().fModelInfo; | |
if (modelInfo != null) { | |
// if the current java model corresponds to a different workspace, | |
// try to close it | |
if (!modelInfo.workspace.equals(workspace)) { | |
try { | |
modelInfo.fJavaModel.close(); | |
} catch (JavaModelException e) { | |
Assert.isTrue(false, Util.bind("element.onlyOneJavaModel"/*nonNLS*/)); | |
return null; | |
} | |
} | |
} | |
if (modelInfo == null || modelInfo.workspace.equals(workspace)) { | |
return new JavaModel(workspace); | |
} else { | |
Assert.isTrue(false, Util.bind("element.onlyOneJavaModel"/*nonNLS*/)); | |
return null; | |
} | |
} | |
/** | |
* Returns the singleton JavaModelManager | |
*/ | |
public static synchronized JavaModelManager getJavaModelManager() { | |
if (fgManager == null) { | |
fgManager= new JavaModelManager(); | |
} | |
return fgManager; | |
} | |
/** | |
* Returns the last built state for the given project, or null if there is none. | |
* Deserializes the state if necessary. | |
* | |
* @private for use by image builder and evaluation support only | |
*/ | |
public IState getLastBuiltState(IProject project, IProgressMonitor monitor) { | |
PerProjectInfo info= getPerProjectInfo(project); | |
IState state= info.getLastBuiltState(); | |
if (state == null && JavaBuilder.SAVE_ENABLED && !info.triedRead) { | |
info.triedRead= true; | |
try { | |
if (monitor != null) monitor.subTask(Util.bind("build.readStateProgress"/*nonNLS*/, project.getName())); | |
state= readState(info); | |
info.setLastBuiltState(state); | |
} catch (CoreException e) { | |
e.printStackTrace(); | |
} | |
} | |
return state; | |
} | |
/** | |
* Returns the last built state for the given project, or null if there is none. | |
* Deserializes the state if necessary. | |
* | |
*/ | |
public INameEnvironment getNameEnvironment(IProject project) { | |
StateImpl state= (StateImpl) getLastBuiltState(project, null); | |
if (state == null) | |
return null; | |
BuilderEnvironment env = new BuilderEnvironment(new BatchImageBuilder(state)); | |
// Fix for 1GB7PUI: ITPJCORE:WINNT - evaluation from type in default package | |
IPackage defaultPackage = state.getDevelopmentContext().getImage().getPackageHandle(PackageImpl.DEFAULT_PACKAGE_PREFIX + project.getName(), true); | |
env.setDefaultPackage(defaultPackage); | |
return env; | |
} | |
/** | |
* Returns the per-project info for the given project. | |
*/ | |
private PerProjectInfo getPerProjectInfo(IProject project) { | |
PerProjectInfo info= (PerProjectInfo) perProjectInfo.get(project); | |
if (info == null) { | |
info= new PerProjectInfo(project); | |
perProjectInfo.put(project, info); | |
} | |
return info; | |
} | |
/** | |
* Returns the File to use for saving and restoring the last built state for the given project. | |
*/ | |
private File getSerializationFile(IProject project) { | |
if (!project.exists()) return null; | |
IPluginDescriptor descr= JavaCore.getJavaCore().getDescriptor(); | |
IPath workingLocation= project.getPluginWorkingLocation(descr); | |
return workingLocation.append("state.dat"/*nonNLS*/).toFile(); | |
} | |
public String getVariablesAsXMLString() throws CoreException { | |
Document document = new DocumentImpl(); | |
Element rootElement = document.createElement("variables"/*nonNLS*/); | |
document.appendChild(rootElement); | |
String[] variables = JavaCore.getClasspathVariableNames(); | |
for (int i= 0; i < variables.length; ++i) { | |
String var = variables[i]; | |
IPath varPath = JavaCore.getClasspathVariable(var); | |
Element varElement= document.createElement("variable"/*nonNLS*/); | |
varElement.setAttribute("name"/*nonNLS*/, var); | |
varElement.setAttribute("path"/*nonNLS*/, varPath.toString()); | |
rootElement.appendChild(varElement); | |
} | |
// produce a String output | |
StringWriter writer = new StringWriter(); | |
try { | |
OutputFormat format = new OutputFormat(); | |
format.setIndenting(true); | |
Serializer serializer = SerializerFactory.getSerializerFactory(Method.XML).makeSerializer(writer, format); | |
serializer.asDOMSerializer().serialize(document); | |
} catch (IOException e) { | |
throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); | |
} | |
return writer.toString(); | |
} | |
/** | |
* Returns the open ZipFile at the given location. If the ZipFile | |
* does not yet exist, it is created, opened, and added to the cache | |
* of open ZipFiles. The location must be a absolute path. | |
* | |
* @exception CoreException If unable to create/open the ZipFile. | |
*/ | |
public ZipFile getZipFile(IPath path) throws CoreException { | |
if (fModelInfo == null) { | |
return null; | |
} | |
String fileSystemPath= null; | |
IWorkspaceRoot root = getJavaModel().getWorkspace().getRoot(); | |
IResource file = root.findMember(path); | |
if (path.isAbsolute() && file != null) { | |
if (file == null || file.getType() != IResource.FILE) { | |
fileSystemPath= path.toOSString(); | |
} else { | |
ensureLocal(file); | |
fileSystemPath= file.getLocation().toOSString(); | |
} | |
} else if (!path.isAbsolute()) { | |
file= root.getFile(path); | |
if (file == null || file.getType() != IResource.FILE) { | |
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind("file.notFound"/*nonNLS*/), null)); | |
} | |
ensureLocal(file); | |
fileSystemPath= file.getLocation().toOSString(); | |
} else { | |
fileSystemPath= path.toOSString(); | |
} | |
try { | |
return new ZipFile(fileSystemPath); | |
} catch (IOException e) { | |
throw new CoreException(new Status(Status.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind("status.IOException"/*nonNLS*/), e)); | |
} | |
} | |
/** | |
* Returns true if the given project is being deleted, otherwise false. | |
* | |
* fix for 1FW67PA | |
*/ | |
public boolean isBeingDeleted(IProject project) { | |
return fProjectsBeingDeleted.contains(project); | |
} | |
/** | |
* Returns true if the firing is enabled | |
*/ | |
public boolean isFiring() { | |
return this.fFire; | |
} | |
public void loadVariables() throws CoreException { | |
String xmlString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty( | |
new QualifiedName(JavaCore.PLUGIN_ID, "variables"/*nonNLS*/)); | |
try { | |
if (xmlString != null) readVariables(xmlString); | |
} catch(IOException e){ | |
return; | |
} | |
} | |
/** | |
* Merged all awaiting deltas. | |
*/ | |
public void mergeDeltas() { | |
Enumeration deltas = null; | |
if (fJavaModelDeltas.isEmpty()) { | |
//deltas = fResourceDeltas.elements(); | |
return; | |
} else { | |
deltas = fJavaModelDeltas.elements(); | |
} | |
if (deltas != null) { | |
JavaElementDelta rootDelta = new JavaElementDelta(getJavaModel()); | |
boolean insertedTree = false; | |
while (deltas.hasMoreElements()) { | |
IJavaElementDelta delta = (IJavaElementDelta)deltas.nextElement(); | |
IJavaElementDelta[] children = delta.getAffectedChildren(); | |
for (int j = 0; j < children.length; j++) { | |
JavaElementDelta projectDelta = (JavaElementDelta) children[j]; | |
rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta); | |
insertedTree = true; | |
} | |
} | |
if (insertedTree){ | |
if (fJavaModelDeltas.isEmpty()) { | |
fResourceDeltas = new Vector(1); | |
fResourceDeltas.addElement(rootDelta); | |
} else { | |
fJavaModelDeltas = new Vector(1); | |
fJavaModelDeltas.addElement(rootDelta); | |
} | |
} | |
else { | |
if (fJavaModelDeltas.isEmpty()) { | |
fResourceDeltas = new Vector(0); | |
} else { | |
fJavaModelDeltas = new Vector(0); | |
} | |
} | |
} | |
} | |
/** | |
* Returns the info for this element without | |
* disturbing the cache ordering. | |
*/ | |
protected Object peekAtInfo(IJavaElement element) { | |
if (fModelInfo == null) { | |
return null; | |
} | |
int type = ((JavaElement) element).fLEType; | |
if (type == IJavaElement.JAVA_MODEL) { | |
return fModelInfo; | |
} else if (type <= IJavaElement.CLASS_FILE) { | |
return fModelInfo.fLRUCache.peek(element); | |
} else { | |
return fModelInfo.fChildrenCache.get(element); | |
} | |
} | |
/** | |
* @see ISaveParticipant | |
*/ | |
public void prepareToSave(ISaveContext context) throws CoreException { | |
} | |
protected void putInfo(IJavaElement element, Object info) { | |
int elementType= ((JavaElement) element).fLEType; | |
if (elementType == IJavaElement.JAVA_MODEL) { | |
fModelInfo= (JavaModelInfo) info; | |
return; | |
} | |
if (elementType <= IJavaElement.CLASS_FILE) { | |
fModelInfo.fLRUCache.put(element, info); | |
} else { | |
fModelInfo.fChildrenCache.put(element, info); | |
} | |
} | |
/** | |
* Reads the build state for the relevant project. | |
*/ | |
protected IState readState(PerProjectInfo info) throws CoreException { | |
File file= getSerializationFile(info.project); | |
if (file == null || !file.exists()) | |
return null; | |
try { | |
DataInputStream in= new DataInputStream(new BufferedInputStream(new FileInputStream(file))); | |
try { | |
String pluginID= in.readUTF(); | |
if (!pluginID.equals(JavaCore.PLUGIN_ID)) | |
throw new IOException(Util.bind("build.wrongFileFormat"/*nonNLS*/)); | |
String kind= in.readUTF(); | |
if (!kind.equals("STATE"/*nonNLS*/)) | |
throw new IOException(Util.bind("build.wrongFileFormat"/*nonNLS*/)); | |
int version= in.readInt(); | |
if (version != 0x0001) | |
throw new IOException(Util.bind("build.unhandledVersionFormat"/*nonNLS*/)); | |
boolean hasState= in.readBoolean(); | |
IState state= null; | |
if (hasState) { | |
state= info.developmentContext.restoreState(info.project, in); | |
} | |
return state; | |
} finally { | |
in.close(); | |
} | |
} catch (Exception e) { | |
//e.printStackTrace(); - silent failure | |
//throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, "Error reading last build state for project "+ info.project.getFullPath(), e)); | |
} | |
return null; | |
} | |
public void readVariables(String xmlString) throws IOException { | |
StringReader reader = new StringReader(xmlString); | |
Element cpElement; | |
try { | |
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |
cpElement = parser.parse(new InputSource(reader)).getDocumentElement(); | |
} catch(SAXException e) { | |
throw new IOException(Util.bind("variable.badFormat"/*nonNLS*/)); | |
} catch(ParserConfigurationException e){ | |
reader.close(); | |
throw new IOException(Util.bind("variable.badFormat"/*nonNLS*/)); | |
} finally { | |
reader.close(); | |
} | |
if (!cpElement.getNodeName().equalsIgnoreCase("variables"/*nonNLS*/)) { | |
throw new IOException(Util.bind("variable.badFormat"/*nonNLS*/)); | |
} | |
NodeList list= cpElement.getChildNodes(); | |
Vector variables = new Vector(); | |
int length= list.getLength(); | |
for (int i= 0; i < length; ++i) { | |
Node node= list.item(i); | |
short type= node.getNodeType(); | |
if (type == Node.ELEMENT_NODE) { | |
Element element= (Element) node; | |
if (element.getNodeName().equalsIgnoreCase("variable"/*nonNLS*/)) { | |
String varName = element.getAttribute("name"/*nonNLS*/); | |
String varPath = element.getAttribute("path"/*nonNLS*/); | |
try { | |
JavaCore.setClasspathVariable(varName, new Path(varPath), null); | |
} catch(JavaModelException e){ | |
} catch(RuntimeException e){ | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Registers the given delta with this manager. This API is to be | |
* used to registerd deltas that are created explicitly by the Java | |
* Model. Deltas created as translations of <code>IResourceDeltas</code> | |
* are to be registered with <code>#registerResourceDelta</code>. | |
*/ | |
protected void registerJavaModelDelta(IJavaElementDelta delta) { | |
fJavaModelDeltas.addElement(delta); | |
} | |
/** | |
* Registers the given delta with this manager. This API is to be | |
* used to register deltas that are created as a side effect | |
* of an <code>IResourceDelta</code>. As <code>IResourceDelta</code>s | |
* are received by the Java Model, they are translated into | |
* <code>IJavaElementDelta</code>s. This is where the translations | |
* are registered. | |
*/ | |
public void registerResourceDelta(IJavaElementDelta delta) { | |
fResourceDeltas.addElement(delta); | |
} | |
/** | |
* removeElementChangedListener method comment. | |
*/ | |
public void removeElementChangedListener(IElementChangedListener listener) { | |
fElementChangedListeners.removeElement(listener); | |
} | |
protected void removeInfo(IJavaElement element) { | |
if (fModelInfo == null) { | |
return; | |
} | |
if (((JavaElement) element).fLEType <= IJavaElement.CLASS_FILE) { | |
fModelInfo.fLRUCache.remove(element); | |
} else { | |
fModelInfo.fChildrenCache.remove(element); | |
} | |
} | |
void removePerProjectInfo(JavaProject javaProject) { | |
IProject project = javaProject.getProject(); | |
PerProjectInfo info= (PerProjectInfo) perProjectInfo.get(project); | |
if (info != null) { | |
perProjectInfo.remove(project); | |
} | |
} | |
/** | |
* Notifies this Java Model Manager that some resource changes have happened | |
* on the platform, and that the Java Model should update any required | |
* internal structures such that its elements remain consistent. | |
* Translates <code>IResourceDeltas</code> into <code>IJavaElementDeltas</code>. | |
* | |
* @see IResourceDelta | |
* @see IResource | |
*/ | |
public void resourceChanged(IResourceChangeEvent event) { | |
if (event.getSource() instanceof IWorkspace) { | |
IResource resource = event.getResource(); | |
IResourceDelta delta = event.getDelta(); | |
switch(event.getType()){ | |
case IResourceChangeEvent.PRE_DELETE : | |
try { | |
if(resource.getType() == IResource.PROJECT | |
&& ((IProject) resource).hasNature(JavaCore.NATURE_ID)) { | |
this.deleting((IProject)resource); | |
} | |
} catch(CoreException e){ | |
} | |
break; | |
case IResourceChangeEvent.PRE_AUTO_BUILD : | |
if(delta != null) { | |
this.checkProjectBeingAdded(delta); | |
DeltaProcessor.checkProjectPropertyFileUpdate(delta, null); // will close project if affected by the property file change | |
} | |
break; | |
case IResourceChangeEvent.POST_CHANGE : | |
if (delta != null) { | |
try { | |
IJavaElementDelta[] translatedDeltas = fDeltaProcessor.processResourceDelta(delta); | |
if (translatedDeltas.length > 0) { | |
for (int i= 0; i < translatedDeltas.length; i++) { | |
registerResourceDelta(translatedDeltas[i]); | |
} | |
} | |
fire(); | |
} finally { | |
// fix for 1FWIAEQ: ITPJCORE:ALL - CRITICAL - "projects being deleted" cache not cleaned up when solution deleted | |
if (!fProjectsBeingDeleted.isEmpty()) { | |
fProjectsBeingDeleted= new Vector(1); | |
} | |
} | |
} | |
break; | |
} | |
} | |
} | |
/** | |
* @see ISaveParticipant | |
*/ | |
public void rollback(ISaveContext context){ | |
} | |
/** | |
* Runs a Java Model Operation | |
*/ | |
public void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException { | |
boolean wasFiring = isFiring(); | |
try { | |
if (wasFiring) stopDeltas(); | |
if (operation.isReadOnly()) { | |
operation.run(monitor); | |
} else { | |
// use IWorkspace.run(...) to ensure that a build will be done in autobuild mode | |
this.getJavaModel().getWorkspace().run(operation, monitor); | |
} | |
} catch (CoreException ce) { | |
if (ce instanceof JavaModelException) { | |
throw (JavaModelException)ce; | |
} else { | |
if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) { | |
Throwable e= ce.getStatus().getException(); | |
if (e instanceof JavaModelException) { | |
throw (JavaModelException) e; | |
} | |
} | |
throw new JavaModelException(ce); | |
} | |
} finally { | |
// fire any registered deltas | |
if (wasFiring){ | |
startDeltas(); | |
fire(); | |
} | |
} | |
} | |
private void saveBuildState() throws CoreException { | |
if (!JavaBuilder.SAVE_ENABLED) | |
return; | |
Vector vStats= null; // lazy initialized | |
for (Enumeration enum= perProjectInfo.elements(); enum.hasMoreElements();) { | |
PerProjectInfo info= (PerProjectInfo) enum.nextElement(); | |
try { | |
saveStateIfNecessary(info); | |
} catch (CoreException e) { | |
if (vStats == null) | |
vStats= new Vector(); | |
vStats.addElement(e.getStatus()); | |
} | |
} | |
if (vStats != null) { | |
IStatus[] stats= new IStatus[vStats.size()]; | |
vStats.copyInto(stats); | |
throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID, IStatus.ERROR, stats, Util.bind("build.cannotSaveStates"/*nonNLS*/), null)); | |
} | |
} | |
/** | |
* Saves the built state for the project. | |
*/ | |
private void saveState(PerProjectInfo info) throws CoreException { | |
if (VERBOSE) System.out.println(Util.bind("build.saveStateProgress"/*nonNLS*/, info.project.getName())); | |
long t= System.currentTimeMillis(); | |
File file= getSerializationFile(info.project); | |
if (file == null) return; | |
try { | |
DataOutputStream out= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); | |
try { | |
out.writeUTF(JavaCore.PLUGIN_ID); | |
out.writeUTF("STATE"/*nonNLS*/); | |
out.writeInt(0x0001); | |
IState state= info.getLastBuiltState(); | |
if (state == null) { | |
out.writeBoolean(false); | |
} else { | |
out.writeBoolean(true); | |
info.developmentContext.saveState(state, out); | |
} | |
} finally { | |
out.close(); | |
} | |
} catch (RuntimeException e) { | |
try { | |
file.delete(); | |
} catch(SecurityException se){ | |
} | |
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, Util.bind("build.cannotSaveState"/*nonNLS*/, info.project.getName()), e)); | |
} catch (IOException e) { | |
try { | |
file.delete(); | |
} catch(SecurityException se){ | |
} | |
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, Util.bind("build.cannotSaveState"/*nonNLS*/, info.project.getName()), e)); | |
} | |
t= System.currentTimeMillis() - t; | |
if (VERBOSE) System.out.println(Util.bind("build.saveStateComplete"/*nonNLS*/, String.valueOf(t))); | |
} | |
/** | |
* Saves the built state for the project if it has been changed since last save. | |
*/ | |
private void saveStateIfNecessary(PerProjectInfo info) throws CoreException { | |
IState state= info.getLastBuiltState(); | |
if (state == null) { | |
saveState(info); | |
info.savedStateFingerprint= null; | |
} else { | |
byte[] fingerprint= state.getFingerprint(); | |
if (Util.compare(fingerprint, info.savedStateFingerprint) != 0) { | |
saveState(info); | |
info.savedStateFingerprint= fingerprint; | |
} | |
} | |
} | |
public void saveVariables() throws CoreException { | |
ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty( | |
new QualifiedName(JavaCore.PLUGIN_ID, "variables"/*nonNLS*/), | |
getVariablesAsXMLString()); | |
} | |
/** | |
* @see ISaveParticipant | |
*/ | |
public void saving(ISaveContext context) throws CoreException { | |
this.saveVariables(); | |
if (context.getKind() == ISaveContext.FULL_SAVE){ | |
this.saveBuildState(); // build state | |
} | |
} | |
/** | |
* Record the order in which to build the java projects (batch build). This order is based | |
* on the projects classpath settings. | |
*/ | |
protected void setBuildOrder(String[] javaBuildOrder) throws JavaModelException { | |
// optional behaviour | |
if (!JavaCore.COMPUTE.equals(JavaCore.getOptions().get(JavaCore.CORE_JAVA_BUILD_ORDER))) return; | |
if (javaBuildOrder == null || javaBuildOrder.length <= 1) return; | |
IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
IWorkspaceDescription description = workspace.getDescription(); | |
String[] wksBuildOrder = description.getBuildOrder(); | |
String[] newOrder; | |
if (wksBuildOrder == null){ | |
newOrder = javaBuildOrder; | |
} else { | |
// remove projects which are already mentionned in java builder order | |
int javaCount = javaBuildOrder.length; | |
Hashtable newSet = new Hashtable(javaCount); // create a set for fast check | |
for (int i = 0; i < javaCount; i++){ | |
newSet.put(javaBuildOrder[i], javaBuildOrder[i]); | |
} | |
int removed = 0; | |
int oldCount = wksBuildOrder.length; | |
for (int i = 0; i < oldCount; i++){ | |
if (newSet.containsKey(wksBuildOrder[i])){ | |
wksBuildOrder[i] = null; | |
removed++; | |
} | |
} | |
// add Java ones first | |
newOrder = new String[oldCount - removed + javaCount]; | |
System.arraycopy(javaBuildOrder, 0, newOrder, 0, javaCount); // java projects are built first | |
// copy previous items in their respective order | |
int index = javaCount; | |
for (int i = 0; i < oldCount; i++){ | |
if (wksBuildOrder[i] != null){ | |
newOrder[index++] = wksBuildOrder[i]; | |
} | |
} | |
} | |
// commit the new build order out | |
description.setBuildOrder(newOrder); | |
try { | |
workspace.setDescription(description); | |
} catch(CoreException e){ | |
throw new JavaModelException(e); | |
} | |
} | |
/** | |
* Sets the last built state for the given project, or null to reset it. | |
*/ | |
public void setLastBuiltState(IProject project, IState state) { | |
PerProjectInfo info= getPerProjectInfo(project); | |
info.triedRead= true; // no point trying to re-read once using setter | |
info.developmentContext.setCurrentState(state); | |
} | |
public void shutdown () { | |
if (fDeltaProcessor.indexManager != null){ // no more indexing | |
fDeltaProcessor.indexManager.shutdown(); | |
fDeltaProcessor.indexManager = null; | |
} | |
try { | |
IJavaModel model = this.getJavaModel(); | |
if (model != null) { | |
model.close(); | |
} | |
} catch (JavaModelException e) { | |
} | |
} | |
/** | |
* Turns the firing mode to on. That is, deltas that are/have been | |
* registered will be fired. | |
*/ | |
public void startDeltas() { | |
fFire= true; | |
} | |
/** | |
* Turns the firing mode to off. That is, deltas that are/have been | |
* registered will not be fired until deltas are started again. | |
*/ | |
public void stopDeltas() { | |
fFire= false; | |
} | |
} |