| /******************************************************************************* |
| * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v0.5 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v05.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jdt.internal.core; |
| |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.resources.*; |
| |
| import org.eclipse.jdt.core.*; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.internal.codeassist.CompletionEngine; |
| import org.eclipse.jdt.internal.codeassist.SelectionEngine; |
| import org.eclipse.jdt.internal.compiler.Compiler; |
| import org.eclipse.jdt.internal.core.search.AbstractSearchScope; |
| import org.eclipse.jdt.internal.core.search.indexing.*; |
| |
| import org.eclipse.jdt.internal.core.builder.JavaBuilder; |
| import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy; |
| |
| import java.io.*; |
| import java.util.*; |
| import java.util.zip.ZipFile; |
| import javax.xml.parsers.*; |
| import org.w3c.dom.*; |
| import org.xml.sax.*; |
| |
| /** |
| * 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 ISaveParticipant { |
| |
| /** |
| * Unique handle onto the JavaModel |
| */ |
| private final JavaModel javaModel = new JavaModel(); |
| |
| /** |
| * Classpath variables pool |
| */ |
| private static HashMap Variables = new HashMap(5); |
| public static HashSet OptionNames = new HashSet(20); |
| public final static String CP_VARIABLE_PREFERENCES_PREFIX = JavaCore.PLUGIN_ID+".classpathVariable."; //$NON-NLS-1$ |
| public final static String CP_VARIABLE_IGNORE = " ##<cp var ignore>## "; //$NON-NLS-1$ |
| |
| /** |
| * Classpath containers pool |
| */ |
| public static Map Containers = new HashMap(5); |
| |
| /** |
| * Flag indicating whether resource may be written (false during post-change) |
| */ |
| public static boolean IsResourceTreeLocked; |
| |
| /** |
| * Name of the extension point for contributing classpath variable initializers |
| */ |
| public static final String CPVARIABLE_INITIALIZER_EXTPOINT_ID = "classpathVariableInitializer" ; //$NON-NLS-1$ |
| |
| /** |
| * Name of the extension point for contributing classpath container initializers |
| */ |
| public static final String CPCONTAINER_INITIALIZER_EXTPOINT_ID = "classpathContainerInitializer" ; //$NON-NLS-1$ |
| |
| /** |
| * Name of the extension point for contributing a source code formatter |
| */ |
| public static final String FORMATTER_EXTPOINT_ID = "codeFormatter" ; //$NON-NLS-1$ |
| |
| /** |
| * Special value used for recognizing ongoing initialization and breaking initialization cycles |
| */ |
| public final static IPath VariableInitializationInProgress = new Path("Variable Initialization In Progress"); //$NON-NLS-1$ |
| public final static IClasspathContainer ContainerInitializationInProgress = new IClasspathContainer() { |
| public IClasspathEntry[] getClasspathEntries() { return null; } |
| public String getDescription() { return null; } |
| public int getKind() { return 0; } |
| public IPath getPath() { return null; } |
| }; |
| |
| private static final String INDEX_MANAGER_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager" ; //$NON-NLS-1$ |
| private static final String COMPILER_DEBUG = JavaCore.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$ |
| private static final String JAVAMODEL_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$ |
| private static final String CP_RESOLVE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution" ; //$NON-NLS-1$ |
| private static final String ZIP_ACCESS_DEBUG = JavaCore.PLUGIN_ID + "/debug/zipaccess" ; //$NON-NLS-1$ |
| private static final String DELTA_DEBUG =JavaCore.PLUGIN_ID + "/debug/javadelta" ; //$NON-NLS-1$ |
| private static final String HIERARCHY_DEBUG = JavaCore.PLUGIN_ID + "/debug/hierarchy" ; //$NON-NLS-1$ |
| private static final String BUILDER_DEBUG = JavaCore.PLUGIN_ID + "/debug/builder" ; //$NON-NLS-1$ |
| private static final String COMPLETION_DEBUG = JavaCore.PLUGIN_ID + "/debug/completion" ; //$NON-NLS-1$ |
| private static final String SELECTION_DEBUG = JavaCore.PLUGIN_ID + "/debug/selection" ; //$NON-NLS-1$ |
| private static final String SHARED_WC_DEBUG = JavaCore.PLUGIN_ID + "/debug/sharedworkingcopy" ; //$NON-NLS-1$ |
| private static final String SEARCH_DEBUG = JavaCore.PLUGIN_ID + "/debug/search" ; //$NON-NLS-1$ |
| |
| public final static IWorkingCopy[] NoWorkingCopy = new IWorkingCopy[0]; |
| |
| /** |
| * Returns whether the given full path (for a package) conflicts with the output location |
| * of the given project. |
| */ |
| public static boolean conflictsWithOutputLocation(IPath folderPath, JavaProject project) { |
| try { |
| IPath outputLocation = project.getOutputLocation(); |
| if (outputLocation == null) { |
| // in doubt, there is a conflict |
| return true; |
| } |
| if (outputLocation.isPrefixOf(folderPath)) { |
| // only allow nesting in outputlocation if there is a corresponding source folder |
| return project.getClasspathEntryFor(outputLocation) == null; |
| } |
| return false; |
| } catch (JavaModelException e) { |
| // in doubt, there is a conflict |
| return true; |
| } |
| } |
| |
| /** |
| * Returns the Java element corresponding to the given resource, or |
| * <code>null</code> if unable to associate the given resource |
| * with a Java element. |
| * <p> |
| * The resource must be one of:<ul> |
| * <li>a project - the element returned is the corresponding <code>IJavaProject</code></li> |
| * <li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li> |
| * <li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li> |
| * <li>a <code>.jar</code> file - the element returned is the corresponding <code>IPackageFragmentRoot</code></li> |
| * <li>a folder - the element returned is the corresponding <code>IPackageFragmentRoot</code> |
| * or <code>IPackageFragment</code></li> |
| * <li>the workspace root resource - the element returned is the <code>IJavaModel</code></li> |
| * </ul> |
| * <p> |
| * Creating a Java element has the side effect of creating and opening all of the |
| * element's parents if they are not yet open. |
| */ |
| public static IJavaElement create(IResource resource, IJavaProject project) { |
| if (resource == null) { |
| return null; |
| } |
| int type = resource.getType(); |
| switch (type) { |
| case IResource.PROJECT : |
| return JavaCore.create((IProject) resource); |
| case IResource.FILE : |
| return create((IFile) resource, project); |
| case IResource.FOLDER : |
| return create((IFolder) resource, project); |
| case IResource.ROOT : |
| return JavaCore.create((IWorkspaceRoot) resource); |
| default : |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the Java element corresponding to the given file, its project being the given |
| * project. |
| * Returns <code>null</code> if unable to associate the given file |
| * with a Java element. |
| * |
| * <p>The file must be one of:<ul> |
| * <li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li> |
| * <li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li> |
| * <li>a <code>.jar</code> file - the element returned is the corresponding <code>IPackageFragmentRoot</code></li> |
| * </ul> |
| * <p> |
| * Creating a Java element has the side effect of creating and opening all of the |
| * element's parents if they are not yet open. |
| */ |
| public static IJavaElement create(IFile file, IJavaProject project) { |
| if (file == null) { |
| return null; |
| } |
| if (project == null) { |
| project = JavaCore.create(file.getProject()); |
| } |
| |
| String extension = file.getFileExtension(); |
| if (extension != null) { |
| if (Util.isValidCompilationUnitName(file.getName())) { |
| return createCompilationUnitFrom(file, project); |
| } else if (Util.isValidClassFileName(file.getName())) { |
| return createClassFileFrom(file, project); |
| } else if (extension.equalsIgnoreCase("jar") //$NON-NLS-1$ |
| || extension.equalsIgnoreCase("zip")) { //$NON-NLS-1$ |
| return createJarPackageFragmentRootFrom(file, project); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the package fragment or package fragment root corresponding to the given folder, |
| * its parent or great parent being the given project. |
| * or <code>null</code> if unable to associate the given folder with a Java element. |
| * <p> |
| * Note that a package fragment root is returned rather than a default package. |
| * <p> |
| * Creating a Java element has the side effect of creating and opening all of the |
| * element's parents if they are not yet open. |
| */ |
| public static IJavaElement create(IFolder folder, IJavaProject project) { |
| if (folder == null) { |
| return null; |
| } |
| if (project == null) { |
| project = JavaCore.create(folder.getProject()); |
| } |
| IJavaElement element = determineIfOnClasspath(folder, project); |
| if (conflictsWithOutputLocation(folder.getFullPath(), (JavaProject)project) |
| || (folder.getName().indexOf('.') >= 0 |
| && !(element instanceof IPackageFragmentRoot))) { |
| return null; // only package fragment roots are allowed with dot names |
| } else { |
| return element; |
| } |
| } |
| |
| /** |
| * Creates and returns a class file element for the given <code>.class</code> file, |
| * its project being the given project. Returns <code>null</code> if unable |
| * to recognize the class file. |
| */ |
| public static IClassFile createClassFileFrom(IFile file, IJavaProject project ) { |
| if (file == null) { |
| return null; |
| } |
| if (project == null) { |
| project = JavaCore.create(file.getProject()); |
| } |
| IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project); |
| if (pkg == null) { |
| // fix for 1FVS7WE |
| // not on classpath - make the root its folder, and a default package |
| IPackageFragmentRoot root = project.getPackageFragmentRoot(file.getParent()); |
| pkg = root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME); |
| } |
| return pkg.getClassFile(file.getName()); |
| } |
| |
| /** |
| * Creates and returns a compilation unit element for the given <code>.java</code> |
| * file, its project being the given project. Returns <code>null</code> if unable |
| * to recognize the compilation unit. |
| */ |
| public static ICompilationUnit createCompilationUnitFrom(IFile file, IJavaProject project) { |
| if (file == null) { |
| return null; |
| } |
| if (project == null) { |
| project = JavaCore.create(file.getProject()); |
| } |
| |
| IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project); |
| if (pkg == null) { |
| // fix for 1FVS7WE |
| // not on classpath - make the root its folder, and a default package |
| IPackageFragmentRoot root = project.getPackageFragmentRoot(file.getParent()); |
| pkg = root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME); |
| } |
| return pkg.getCompilationUnit(file.getName()); |
| } |
| |
| /** |
| * Creates and returns a handle for the given JAR file, its project being the given project. |
| * The Java model associated with the JAR's project may be |
| * created as a side effect. |
| * Returns <code>null</code> if unable to create a JAR package fragment root. |
| * (for example, if the JAR file represents a non-Java resource) |
| */ |
| public static IPackageFragmentRoot createJarPackageFragmentRootFrom(IFile file, IJavaProject project) { |
| if (file == null) { |
| return null; |
| } |
| if (project == null) { |
| project = JavaCore.create(file.getProject()); |
| } |
| |
| // Create a jar package fragment root only if on the classpath |
| IPath resourcePath = file.getFullPath(); |
| try { |
| IClasspathEntry[] entries = ((JavaProject)project).getResolvedClasspath(true); |
| for (int i = 0, length = entries.length; i < length; i++) { |
| IClasspathEntry entry = entries[i]; |
| IPath rootPath = entry.getPath(); |
| if (rootPath.equals(resourcePath)) { |
| return project.getPackageFragmentRoot(file); |
| } |
| } |
| } catch (JavaModelException e) { |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the package fragment root represented by the resource, or |
| * the package fragment the given resource is located in, or <code>null</code> |
| * if the given resource is not on the classpath of the given project. |
| */ |
| public static IJavaElement determineIfOnClasspath( |
| IResource resource, |
| IJavaProject project) { |
| |
| IPath resourcePath = resource.getFullPath(); |
| try { |
| IClasspathEntry[] entries = |
| Util.isJavaFileName(resourcePath.lastSegment()) |
| ? project.getRawClasspath() // JAVA file can only live inside SRC folder (on the raw path) |
| : ((JavaProject)project).getResolvedClasspath(true); |
| |
| for (int i = 0; i < entries.length; i++) { |
| IClasspathEntry entry = entries[i]; |
| if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue; |
| IPath rootPath = entry.getPath(); |
| if (rootPath.equals(resourcePath)) { |
| return project.getPackageFragmentRoot(resource); |
| } else if (rootPath.isPrefixOf(resourcePath)) { |
| IPackageFragmentRoot root = ((JavaProject) project).getPackageFragmentRoot(rootPath); |
| if (root == null) return null; |
| IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount()); |
| if (resource.getType() == IResource.FILE) { |
| // if the resource is a file, then remove the last segment which |
| // is the file name in the package |
| pkgPath = pkgPath.removeLastSegments(1); |
| } |
| String pkgName = Util.packageName(pkgPath); |
| if (pkgName == null || JavaConventions.validatePackageName(pkgName).getSeverity() == IStatus.ERROR) { |
| return null; |
| } |
| return root.getPackageFragment(pkgName); |
| } |
| } |
| } catch (JavaModelException npe) { |
| return null; |
| } |
| return null; |
| } |
| |
| /** |
| * The singleton manager |
| */ |
| private final static JavaModelManager Manager= new JavaModelManager(); |
| |
| /** |
| * Infos cache. |
| */ |
| protected JavaModelCache cache = new JavaModelCache(); |
| |
| /** |
| * Set of elements which are out of sync with their buffers. |
| */ |
| protected Map elementsOutOfSynchWithBuffers = new HashMap(11); |
| |
| /** |
| * Turns delta firing on/off. By default it is on. |
| */ |
| private boolean isFiring= true; |
| |
| /** |
| * Queue of deltas created explicily by the Java Model that |
| * have yet to be fired. |
| */ |
| private ArrayList javaModelDeltas= new ArrayList(); |
| |
| /** |
| * Collection of listeners for Java element deltas |
| */ |
| private IElementChangedListener[] elementChangedListeners = new IElementChangedListener[5]; |
| private int[] elementChangedListenerMasks = new int[5]; |
| private int elementChangedListenerCount = 0; |
| public int currentChangeEventType = ElementChangedEvent.PRE_AUTO_BUILD; |
| public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with ElementChangedEvent event masks |
| |
| |
| |
| /** |
| * Used to convert <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s. |
| */ |
| public final DeltaProcessor deltaProcessor = new DeltaProcessor(this); |
| /** |
| * Used to update the JavaModel for <code>IJavaElementDelta</code>s. |
| */ |
| private final ModelUpdater modelUpdater =new ModelUpdater(); |
| /** |
| * Workaround for bug 15168 circular errors not reported |
| * This is a cache of the projects before any project addition/deletion has started. |
| */ |
| public IJavaProject[] javaProjectsCache; |
| |
| |
| /** |
| * Local Java workspace properties file name (generated inside JavaCore plugin state location) |
| */ |
| private static final String WKS_PROP_FILENAME= "workspace.properties"; //$NON-NLS-1$ |
| |
| /** |
| * Name of the handle id attribute in a Java marker |
| */ |
| private static final String ATT_HANDLE_ID= "org.eclipse.jdt.internal.core.JavaModelManager.handleId"; //$NON-NLS-1$ |
| |
| /** |
| * Table from IProject to PerProjectInfo. |
| */ |
| protected Map perProjectInfo = new HashMap(5); |
| |
| /** |
| * A map from ICompilationUnit to IWorkingCopy |
| * of the shared working copies. |
| */ |
| public Map sharedWorkingCopies = new HashMap(); |
| |
| /** |
| * A weak set of the known scopes. |
| */ |
| protected WeakHashMap scopes = new WeakHashMap(); |
| |
| static class PerProjectInfo { |
| IProject project; |
| Object savedState; |
| boolean triedRead; |
| IClasspathEntry[] classpath; |
| IClasspathEntry[] lastResolvedClasspath; |
| PerProjectInfo(IProject project) { |
| this.triedRead = false; |
| this.savedState = null; |
| this.project = project; |
| } |
| }; |
| public static boolean VERBOSE = false; |
| public static boolean CP_RESOLVE_VERBOSE = false; |
| public static boolean ZIP_ACCESS_VERBOSE = false; |
| |
| /** |
| * A cache of opened zip files per thread. |
| * (map from Thread to map of IPath to java.io.ZipFile) |
| */ |
| private HashMap zipFiles = new HashMap(); |
| |
| |
| /** |
| * Update the classpath variable cache |
| */ |
| public static class PluginPreferencesListener implements Preferences.IPropertyChangeListener { |
| /** |
| * @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(PropertyChangeEvent) |
| */ |
| public void propertyChange(Preferences.PropertyChangeEvent event) { |
| |
| String propertyName = event.getProperty(); |
| if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) { |
| |
| // update path cache |
| String varName = propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length()); |
| String newValue = (String)event.getNewValue(); |
| if (newValue == null || newValue.equals(CP_VARIABLE_IGNORE)) { |
| Variables.remove(varName); |
| } else { |
| Variables.put(varName, new Path(newValue)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Line separator to use throughout the JavaModel for any source edit operation |
| */ |
| // public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$ |
| /** |
| * Constructs a new JavaModelManager |
| */ |
| private JavaModelManager() { |
| } |
| |
| /** |
| * @deprecated - discard once debug has converted to not using it |
| */ |
| public void addElementChangedListener(IElementChangedListener listener) { |
| this.addElementChangedListener(listener, ElementChangedEvent.POST_CHANGE | ElementChangedEvent.POST_RECONCILE); |
| } |
| /** |
| * addElementChangedListener method comment. |
| * Need to clone defensively the listener information, in case some listener is reacting to some notification iteration by adding/changing/removing |
| * any of the other (i.e. it deregisters itself). |
| */ |
| public void addElementChangedListener(IElementChangedListener listener, int eventMask) { |
| for (int i = 0; i < this.elementChangedListenerCount; i++){ |
| if (this.elementChangedListeners[i].equals(listener)){ |
| |
| // only clone the masks, since we could be in the middle of notifications and one listener decide to change |
| // any event mask of another listeners (yet not notified). |
| int cloneLength = this.elementChangedListenerMasks.length; |
| System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[cloneLength], 0, cloneLength); |
| this.elementChangedListenerMasks[i] = eventMask; // could be different |
| return; |
| } |
| } |
| // may need to grow, no need to clone, since iterators will have cached original arrays and max boundary and we only add to the end. |
| int length; |
| if ((length = this.elementChangedListeners.length) == this.elementChangedListenerCount){ |
| System.arraycopy(this.elementChangedListeners, 0, this.elementChangedListeners = new IElementChangedListener[length*2], 0, length); |
| System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[length*2], 0, length); |
| } |
| this.elementChangedListeners[this.elementChangedListenerCount] = listener; |
| this.elementChangedListenerMasks[this.elementChangedListenerCount] = eventMask; |
| this.elementChangedListenerCount++; |
| } |
| |
| /** |
| * Starts caching ZipFiles. |
| * Ignores if there are already clients. |
| */ |
| public synchronized void cacheZipFiles() { |
| Thread currentThread = Thread.currentThread(); |
| if (this.zipFiles.get(currentThread) != null) return; |
| this.zipFiles.put(currentThread, new HashMap()); |
| } |
| public synchronized void closeZipFile(ZipFile zipFile) { |
| if (zipFile == null) return; |
| if (this.zipFiles.get(Thread.currentThread()) != null) { |
| return; // zip file will be closed by call to flushZipFiles |
| } |
| try { |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] Closing ZipFile on " +zipFile.getName()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| zipFile.close(); |
| } catch (IOException e) { |
| } |
| } |
| |
| |
| |
| /** |
| * Configure the plugin with respect to option settings defined in ".options" file |
| */ |
| public void configurePluginDebugOptions(){ |
| if(JavaCore.getPlugin().isDebugging()){ |
| String option = Platform.getDebugOption(INDEX_MANAGER_DEBUG); |
| if(option != null) IndexManager.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(COMPILER_DEBUG); |
| if(option != null) Compiler.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(JAVAMODEL_DEBUG); |
| if(option != null) JavaModelManager.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(SHARED_WC_DEBUG); |
| if(option != null) CompilationUnit.SHARED_WC_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(CP_RESOLVE_DEBUG); |
| if(option != null) JavaModelManager.CP_RESOLVE_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(ZIP_ACCESS_DEBUG); |
| if(option != null) JavaModelManager.ZIP_ACCESS_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(DELTA_DEBUG); |
| if(option != null) DeltaProcessor.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(HIERARCHY_DEBUG); |
| if(option != null) TypeHierarchy.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(BUILDER_DEBUG); |
| if(option != null) JavaBuilder.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(COMPLETION_DEBUG); |
| if(option != null) CompletionEngine.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(SELECTION_DEBUG); |
| if(option != null) SelectionEngine.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| |
| option = Platform.getDebugOption(SEARCH_DEBUG); |
| if(option != null) SearchEngine.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$ |
| } |
| } |
| |
| |
| |
| /** |
| * @see ISaveParticipant |
| */ |
| public void doneSaving(ISaveContext context){ |
| } |
| |
| /** |
| * Fire Java Model delta, flushing them after the fact after post_change notification. |
| * If the firing mode has been turned off, this has no effect. |
| */ |
| public void fire(JavaElementDelta customDelta, int originalEventType) { |
| |
| if (this.isFiring) { |
| |
| int eventType; |
| |
| /* DEFAULT event type is used when operation doesn't know actual event type and needed to fire immediately: |
| * e.g. non-resource modifying operation, create/destroy shared working copies |
| * |
| * this is mapped to a POST-change + PRE-build change for all interested listeners |
| */ |
| if (originalEventType == DEFAULT_CHANGE_EVENT){ |
| eventType = ElementChangedEvent.POST_CHANGE; |
| } else { |
| eventType = originalEventType; |
| } |
| |
| JavaElementDelta deltaToNotify; |
| if (customDelta == null){ |
| this.mergeDeltas(); |
| if (this.javaModelDeltas.size() > 0){ |
| |
| // cannot be more than 1 after merge |
| deltaToNotify = (JavaElementDelta)this.javaModelDeltas.get(0); |
| |
| // empty the queue only after having fired final volley of deltas and no custom deltas was superposed |
| if (eventType == ElementChangedEvent.POST_CHANGE){ |
| // flush now so as to keep listener reactions to post their own deltas for subsequent iteration |
| this.flush(); |
| } |
| } else { |
| return; |
| } |
| } else { |
| deltaToNotify = customDelta; |
| } |
| |
| // Refresh internal scopes |
| Iterator scopes = this.scopes.keySet().iterator(); |
| while (scopes.hasNext()) { |
| AbstractSearchScope scope = (AbstractSearchScope)scopes.next(); |
| scope.processDelta(deltaToNotify); |
| } |
| |
| // Notification |
| |
| // Important: if any listener reacts to notification by updating the listeners list or mask, these lists will |
| // be duplicated, so it is necessary to remember original lists in a variable (since field values may change under us) |
| IElementChangedListener[] listeners = this.elementChangedListeners; |
| int[] listenerMask = this.elementChangedListenerMasks; |
| int listenerCount = this.elementChangedListenerCount; |
| |
| // in case using a DEFAULT change event, will notify also all listeners also interested in PRE-build events |
| if (originalEventType == DEFAULT_CHANGE_EVENT){ |
| if (DeltaProcessor.VERBOSE){ |
| System.out.println("FIRING PRE_AUTO_BUILD Delta ["+Thread.currentThread()+"]:\n" + deltaToNotify);//$NON-NLS-1$//$NON-NLS-2$ |
| } |
| final ElementChangedEvent extraEvent = new ElementChangedEvent(deltaToNotify, ElementChangedEvent.PRE_AUTO_BUILD); |
| for (int i= 0; i < listenerCount; i++) { |
| if ((listenerMask[i] & ElementChangedEvent.PRE_AUTO_BUILD) != 0){ |
| final IElementChangedListener listener = listeners[i]; |
| // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief |
| Platform.run(new ISafeRunnable() { |
| public void handleException(Throwable exception) { |
| Util.log(exception, "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$ |
| } |
| public void run() throws Exception { |
| listener.elementChanged(extraEvent); |
| } |
| }); |
| } |
| } |
| } |
| |
| // regular notification |
| if (DeltaProcessor.VERBOSE){ |
| String type = "";//$NON-NLS-1$ |
| switch (eventType) { |
| case ElementChangedEvent.POST_CHANGE: |
| type = "POST_CHANGE"; //$NON-NLS-1$ |
| break; |
| case ElementChangedEvent.PRE_AUTO_BUILD: |
| type = "PRE_AUTO_BUILD"; //$NON-NLS-1$ |
| break; |
| case ElementChangedEvent.POST_RECONCILE: |
| type = "POST_RECONCILE"; //$NON-NLS-1$ |
| break; |
| } |
| System.out.println("FIRING " + type + " Delta ["+Thread.currentThread()+"]:\n" + deltaToNotify);//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ |
| } |
| final ElementChangedEvent event = new ElementChangedEvent(deltaToNotify, eventType); |
| for (int i= 0; i < listenerCount; i++) { |
| if ((listenerMask[i] & eventType) != 0){ |
| // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief |
| final IElementChangedListener listener = listeners[i]; |
| Platform.run(new ISafeRunnable() { |
| public void handleException(Throwable exception) { |
| Util.log(exception, "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$ |
| } |
| public void run() throws Exception { |
| listener.elementChanged(event); |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Flushes all deltas without firing them. |
| */ |
| protected void flush() { |
| this.javaModelDeltas= new ArrayList(); |
| } |
| |
| /** |
| * Flushes ZipFiles cache if there are no more clients. |
| */ |
| public synchronized void flushZipFiles() { |
| Thread currentThread = Thread.currentThread(); |
| HashMap map = (HashMap)this.zipFiles.remove(currentThread); |
| if (map == null) return; |
| Iterator iterator = map.values().iterator(); |
| while (iterator.hasNext()) { |
| try { |
| ZipFile zipFile = (ZipFile)iterator.next(); |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + currentThread + ") [JavaModelManager.flushZipFiles()] Closing ZipFile on " +zipFile.getName()); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| zipFile.close(); |
| } catch (IOException e) { |
| } |
| } |
| |
| } |
| |
| /** |
| * Retrieve the registered classpath container initializer for a given container ID |
| */ |
| public static ClasspathContainerInitializer getClasspathContainerInitializer(String containerID){ |
| |
| Plugin jdtCorePlugin = JavaCore.getPlugin(); |
| if (jdtCorePlugin == null) return null; |
| |
| IExtensionPoint extension = jdtCorePlugin.getDescriptor().getExtensionPoint(CPCONTAINER_INITIALIZER_EXTPOINT_ID); |
| if (extension != null) { |
| IExtension[] extensions = extension.getExtensions(); |
| for(int i = 0; i < extensions.length; i++){ |
| IConfigurationElement [] configElements = extensions[i].getConfigurationElements(); |
| IPluginDescriptor plugin = extension.getDeclaringPluginDescriptor(); |
| if (plugin.isPluginActivated()) { |
| |
| for(int j = 0; j < configElements.length; j++){ |
| String initializerID = configElements[j].getAttribute("id"); //$NON-NLS-1$ |
| if (initializerID != null && initializerID.equals(containerID)){ |
| if (JavaModelManager.CP_RESOLVE_VERBOSE) { |
| System.out.println("CPVariable INIT - found initializer: "+containerID +" --> " + configElements[j].getAttribute("class"));//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ |
| } |
| try { |
| Object execExt = configElements[j].createExecutableExtension("class"); //$NON-NLS-1$ |
| if (execExt instanceof ClasspathContainerInitializer){ |
| return (ClasspathContainerInitializer)execExt; |
| } |
| } catch(CoreException e) { |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the set of elements which are out of synch with their buffers. |
| */ |
| protected Map getElementsOutOfSynchWithBuffers() { |
| return this.elementsOutOfSynchWithBuffers; |
| } |
| |
| /** |
| * Returns the <code>IJavaElement</code> represented by the |
| * <code>String</code> memento. |
| */ |
| public IJavaElement getHandleFromMemento(String memento) throws JavaModelException { |
| if (memento == null) { |
| return null; |
| } |
| JavaModel model= (JavaModel) getJavaModel(); |
| if (memento.equals("")){ // workspace memento //$NON-NLS-1$ |
| 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(rootPath); |
| } 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 this.deltaProcessor.indexManager; |
| } |
| |
| /** |
| * Returns the info for the element. |
| */ |
| public Object getInfo(IJavaElement element) { |
| return this.cache.getInfo(element); |
| } |
| |
| /** |
| * Returns the handle to the active Java Model. |
| */ |
| public final JavaModel getJavaModel() { |
| return javaModel; |
| } |
| |
| /** |
| * Returns the singleton JavaModelManager |
| */ |
| public final static JavaModelManager getJavaModelManager() { |
| return Manager; |
| } |
| |
| /** |
| * Returns the last built state for the given project, or null if there is none. |
| * Deserializes the state if necessary. |
| * |
| * For use by image builder and evaluation support only |
| */ |
| public Object getLastBuiltState(IProject project, IProgressMonitor monitor) { |
| PerProjectInfo info = getPerProjectInfo(project); |
| if (!info.triedRead) { |
| info.triedRead = true; |
| try { |
| if (monitor != null) |
| monitor.subTask(Util.bind("build.readStateProgress", project.getName())); //$NON-NLS-1$ |
| info.savedState = readState(project); |
| } catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| return info.savedState; |
| } |
| |
| /* |
| * Returns the per-project info for the given project. Create the info if the info doesn't exist. |
| */ |
| private PerProjectInfo getPerProjectInfo(IProject project) { |
| return getPerProjectInfo(project, true /* create info */); |
| } |
| |
| /* |
| * Returns the per-project info for the given project. If specified, create the info if the info doesn't exist. |
| */ |
| synchronized PerProjectInfo getPerProjectInfo(IProject project, boolean create) { |
| PerProjectInfo info= (PerProjectInfo) perProjectInfo.get(project); |
| if (info == null && create) { |
| info= new PerProjectInfo(project); |
| perProjectInfo.put(project, info); |
| } |
| return info; |
| } |
| |
| /* |
| * Returns the per-project info for the given project. |
| * If the info if the info doesn't exist, check for the project existence and create the info. |
| * @throws JavaModelException if the project doesn't exist. |
| */ |
| PerProjectInfo getPerProjectInfoCheckExistence(IProject project) throws JavaModelException { |
| JavaModelManager.PerProjectInfo info = getPerProjectInfo(project, false /* don't create info */); |
| if (info == null) { |
| if (!project.exists()) { |
| throw ((JavaProject)JavaCore.create(project)).newNotPresentException(); |
| } |
| info = getPerProjectInfo(project, true /* create 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").toFile(); //$NON-NLS-1$ |
| } |
| |
| /** |
| * 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 synchronized ZipFile getZipFile(IPath path) throws CoreException { |
| Thread currentThread = Thread.currentThread(); |
| HashMap map = null; |
| ZipFile zipFile; |
| if ((map = (HashMap)this.zipFiles.get(currentThread)) != null |
| && (zipFile = (ZipFile)map.get(path)) != null) { |
| |
| return zipFile; |
| } |
| String fileSystemPath= null; |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IResource file = root.findMember(path); |
| if (path.isAbsolute() && file != null) { |
| if (file == null || file.getType() != IResource.FILE) { |
| fileSystemPath= path.toOSString(); |
| } else { |
| 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"), null)); //$NON-NLS-1$ |
| } |
| fileSystemPath= file.getLocation().toOSString(); |
| } else { |
| fileSystemPath= path.toOSString(); |
| } |
| |
| try { |
| if (ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + currentThread + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + fileSystemPath ); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| zipFile = new ZipFile(fileSystemPath); |
| if (map != null) { |
| map.put(path, zipFile); |
| } |
| return zipFile; |
| } catch (IOException e) { |
| throw new CoreException(new Status(Status.ERROR, JavaCore.PLUGIN_ID, -1, Util.bind("status.IOException"), e)); //$NON-NLS-1$ |
| } |
| } |
| |
| |
| |
| /** |
| * Returns true if the firing is enabled |
| */ |
| public boolean isFiring() { |
| return this.isFiring; |
| } |
| |
| public void loadVariables() throws CoreException { |
| |
| // backward compatibility, consider persistent property |
| QualifiedName qName = new QualifiedName(JavaCore.PLUGIN_ID, "variables"); //$NON-NLS-1$ |
| String xmlString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName); |
| |
| try { |
| if (xmlString != null){ |
| StringReader reader = new StringReader(xmlString); |
| Element cpElement; |
| try { |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| cpElement = parser.parse(new InputSource(reader)).getDocumentElement(); |
| } catch(SAXException e) { |
| return; |
| } catch(ParserConfigurationException e){ |
| return; |
| } finally { |
| reader.close(); |
| } |
| if (cpElement == null) return; |
| if (!cpElement.getNodeName().equalsIgnoreCase("variables")) { //$NON-NLS-1$ |
| return; |
| } |
| ArrayList variableNamesList = new ArrayList(); |
| ArrayList variablePathsList = new ArrayList(); |
| |
| NodeList list= cpElement.getChildNodes(); |
| 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")) { //$NON-NLS-1$ |
| variablePut( |
| element.getAttribute("name"), //$NON-NLS-1$ |
| new Path(element.getAttribute("path"))); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| } catch(IOException e){ |
| } finally { |
| if (xmlString != null){ |
| ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName, null); // flush old one |
| } |
| |
| } |
| |
| // load variables from preferences into cache |
| Preferences preferences = JavaCore.getPlugin().getPluginPreferences(); |
| |
| // only get variable from preferences not set to their default |
| String[] propertyNames = preferences.propertyNames(); |
| int prefixLength = CP_VARIABLE_PREFERENCES_PREFIX.length(); |
| for (int i = 0; i < propertyNames.length; i++){ |
| String propertyName = propertyNames[i]; |
| if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)){ |
| String varName = propertyName.substring(prefixLength); |
| IPath varPath = new Path(preferences.getString(propertyName)); |
| Variables.put(varName, varPath); |
| } |
| } |
| } |
| |
| /** |
| * Merged all awaiting deltas. |
| */ |
| public void mergeDeltas() { |
| if (this.javaModelDeltas.size() <= 1) return; |
| |
| if (DeltaProcessor.VERBOSE) { |
| System.out.println("MERGING " + this.javaModelDeltas.size() + " DELTAS ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| |
| Iterator deltas = this.javaModelDeltas.iterator(); |
| IJavaElement javaModel = this.getJavaModel(); |
| JavaElementDelta rootDelta = new JavaElementDelta(javaModel); |
| boolean insertedTree = false; |
| while (deltas.hasNext()) { |
| JavaElementDelta delta = (JavaElementDelta)deltas.next(); |
| if (DeltaProcessor.VERBOSE) { |
| System.out.println(delta.toString()); |
| } |
| IJavaElement element = delta.getElement(); |
| if (javaModel.equals(element)) { |
| IJavaElementDelta[] children = delta.getAffectedChildren(); |
| for (int j = 0; j < children.length; j++) { |
| JavaElementDelta projectDelta = (JavaElementDelta) children[j]; |
| rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta); |
| insertedTree = true; |
| } |
| } else { |
| rootDelta.insertDeltaTree(element, delta); |
| insertedTree = true; |
| } |
| } |
| if (insertedTree){ |
| this.javaModelDeltas = new ArrayList(1); |
| this.javaModelDeltas.add(rootDelta); |
| } |
| else { |
| this.javaModelDeltas = new ArrayList(0); |
| } |
| } |
| |
| /** |
| * Returns the info for this element without |
| * disturbing the cache ordering. |
| */ |
| protected Object peekAtInfo(IJavaElement element) { |
| return this.cache.peekAtInfo(element); |
| } |
| |
| /** |
| * @see ISaveParticipant |
| */ |
| public void prepareToSave(ISaveContext context) throws CoreException { |
| } |
| |
| protected void putInfo(IJavaElement element, Object info) { |
| this.cache.putInfo(element, info); |
| } |
| |
| /** |
| * Reads the build state for the relevant project. |
| */ |
| protected Object readState(IProject project) throws CoreException { |
| File file = getSerializationFile(project); |
| if (file != null && file.exists()) { |
| 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")); //$NON-NLS-1$ |
| String kind= in.readUTF(); |
| if (!kind.equals("STATE")) //$NON-NLS-1$ |
| throw new IOException(Util.bind("build.wrongFileFormat")); //$NON-NLS-1$ |
| if (in.readBoolean()) |
| return JavaBuilder.readState(in); |
| if (JavaBuilder.DEBUG) |
| System.out.println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$ |
| } finally { |
| in.close(); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, "Error reading last build state for project "+ project.getName(), e)); //$NON-NLS-1$ |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * 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) { |
| this.javaModelDeltas.add(delta); |
| } |
| |
| /** |
| * Remembers the given scope in a weak set |
| * (so no need to remove it: it will be removed by the garbage collector) |
| */ |
| public void rememberScope(AbstractSearchScope scope) { |
| // NB: The value has to be null so as to not create a strong reference on the scope |
| this.scopes.put(scope, null); |
| } |
| |
| /** |
| * removeElementChangedListener method comment. |
| */ |
| public void removeElementChangedListener(IElementChangedListener listener) { |
| |
| for (int i = 0; i < this.elementChangedListenerCount; i++){ |
| |
| if (this.elementChangedListeners[i].equals(listener)){ |
| |
| // need to clone defensively since we might be in the middle of listener notifications (#fire) |
| int length = this.elementChangedListeners.length; |
| IElementChangedListener[] newListeners = new IElementChangedListener[length]; |
| System.arraycopy(this.elementChangedListeners, 0, newListeners, 0, i); |
| int[] newMasks = new int[length]; |
| System.arraycopy(this.elementChangedListenerMasks, 0, newMasks, 0, i); |
| |
| // copy trailing listeners |
| int trailingLength = this.elementChangedListenerCount - i - 1; |
| if (trailingLength > 0){ |
| System.arraycopy(this.elementChangedListeners, i+1, newListeners, i, trailingLength); |
| System.arraycopy(this.elementChangedListenerMasks, i+1, newMasks, i, trailingLength); |
| } |
| |
| // update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto |
| // the original ones) |
| this.elementChangedListeners = newListeners; |
| this.elementChangedListenerMasks = newMasks; |
| this.elementChangedListenerCount--; |
| return; |
| } |
| } |
| } |
| |
| protected void removeInfo(IJavaElement element) { |
| this.cache.removeInfo(element); |
| } |
| |
| void removePerProjectInfo(JavaProject javaProject) { |
| IProject project = javaProject.getProject(); |
| PerProjectInfo info= (PerProjectInfo) perProjectInfo.get(project); |
| if (info != null) { |
| perProjectInfo.remove(project); |
| } |
| } |
| |
| |
| |
| /** |
| * @see ISaveParticipant |
| */ |
| public void rollback(ISaveContext context){ |
| } |
| |
| /** |
| * Runs a Java Model Operation |
| */ |
| public void runOperation(JavaModelOperation operation, IProgressMonitor monitor) throws JavaModelException { |
| |
| int previousDeltaCount = this.javaModelDeltas.size(); |
| try { |
| 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 { |
| // update JavaModel using deltas that were recorded during this operation |
| for (int i = previousDeltaCount, size = this.javaModelDeltas.size(); i < size; i++) { |
| updateJavaModel((IJavaElementDelta)this.javaModelDeltas.get(i)); |
| } |
| |
| // fire only iff: |
| // - the operation did produce some delta(s) |
| // - but the operation has not modified any resource |
| if ((this.javaModelDeltas.size() > previousDeltaCount) |
| && !operation.hasModifiedResource()) { |
| fire(null, JavaModelManager.DEFAULT_CHANGE_EVENT); |
| } // else deltas are fired while processing the resource delta |
| } |
| } |
| |
| private void saveBuildState() throws CoreException { |
| ArrayList vStats= null; // lazy initialized |
| for (Iterator iter = perProjectInfo.values().iterator(); iter.hasNext();) { |
| try { |
| PerProjectInfo info = (PerProjectInfo) iter.next(); |
| if (info.triedRead) |
| saveState(info); |
| } catch (CoreException e) { |
| if (vStats == null) |
| vStats= new ArrayList(); |
| vStats.add(e.getStatus()); |
| } |
| } |
| if (vStats != null) { |
| IStatus[] stats= new IStatus[vStats.size()]; |
| vStats.toArray(stats); |
| throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID, IStatus.ERROR, stats, Util.bind("build.cannotSaveStates"), null)); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Saves the built state for the project. |
| */ |
| private void saveState(PerProjectInfo info) throws CoreException { |
| if (JavaBuilder.DEBUG) |
| System.out.println(Util.bind("build.saveStateProgress", info.project.getName())); //$NON-NLS-1$ |
| File file = getSerializationFile(info.project); |
| if (file == null) return; |
| long t = System.currentTimeMillis(); |
| try { |
| DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); |
| try { |
| out.writeUTF(JavaCore.PLUGIN_ID); |
| out.writeUTF("STATE"); //$NON-NLS-1$ |
| if (info.savedState == null) { |
| out.writeBoolean(false); |
| } else { |
| out.writeBoolean(true); |
| JavaBuilder.writeState(info.savedState, 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", info.project.getName()), e)); //$NON-NLS-1$ |
| } 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", info.project.getName()), e)); //$NON-NLS-1$ |
| } |
| if (JavaBuilder.DEBUG) { |
| t = System.currentTimeMillis() - t; |
| System.out.println(Util.bind("build.saveStateComplete", String.valueOf(t))); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * @see ISaveParticipant |
| */ |
| public void saving(ISaveContext context) throws CoreException { |
| |
| int k = context.getKind(); |
| if (k == ISaveContext.FULL_SAVE){ |
| this.saveBuildState(); // build state |
| } else if (k == ISaveContext.PROJECT_SAVE){ |
| PerProjectInfo info = getPerProjectInfo(context.getProject()); |
| if (info.triedRead) |
| saveState(info); |
| } |
| } |
| |
| /** |
| * 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 |
| // possible value of index 0 is Compute |
| if (!JavaCore.COMPUTE.equals(JavaCore.getOption(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; |
| HashMap newSet = new HashMap(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, Object state) { |
| PerProjectInfo info = getPerProjectInfo(project); |
| info.triedRead = true; // no point trying to re-read once using setter |
| info.savedState = state; |
| if (state == null) { // delete state file to ensure a full build happens if the workspace crashes |
| try { |
| File file = getSerializationFile(project); |
| if (file != null && file.exists()) |
| file.delete(); |
| } catch(SecurityException se) {} |
| } |
| } |
| |
| public void shutdown () { |
| if (this.deltaProcessor.indexManager != null){ // no more indexing |
| this.deltaProcessor.indexManager.shutdown(); |
| } |
| 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() { |
| this.isFiring= 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() { |
| this.isFiring= false; |
| } |
| |
| /** |
| * Update Java Model given some delta |
| */ |
| public void updateJavaModel(IJavaElementDelta customDelta) { |
| |
| if (customDelta == null){ |
| for (int i = 0, length = this.javaModelDeltas.size(); i < length; i++){ |
| IJavaElementDelta delta = (IJavaElementDelta)this.javaModelDeltas.get(i); |
| this.modelUpdater.processJavaDelta(delta); |
| } |
| } else { |
| this.modelUpdater.processJavaDelta(customDelta); |
| } |
| } |
| |
| public static IPath variableGet(String varName){ |
| return (IPath)Variables.get(varName); |
| } |
| |
| public static String[] variableNames(){ |
| int length = Variables.size(); |
| String[] result = new String[length]; |
| Iterator vars = Variables.keySet().iterator(); |
| int index = 0; |
| while (vars.hasNext()) { |
| result[index++] = (String) vars.next(); |
| } |
| return result; |
| } |
| |
| public static void variablePut(String varName, IPath varPath){ |
| |
| // do not write out intermediate initialization value |
| if (varPath == JavaModelManager.VariableInitializationInProgress){ |
| Variables.put(varName, varPath); |
| return; |
| } |
| Preferences preferences = JavaCore.getPlugin().getPluginPreferences(); |
| String varPref = CP_VARIABLE_PREFERENCES_PREFIX+varName; |
| String varString = varPath == null ? CP_VARIABLE_IGNORE : varPath.toString(); |
| preferences.setDefault(varPref, CP_VARIABLE_IGNORE); // use this default to get rid of removed ones |
| preferences.setValue(varPref, varString); |
| JavaCore.getPlugin().savePluginPreferences(); |
| } |
| } |