1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
13 import java.io.BufferedInputStream;
14 import java.io.BufferedOutputStream;
15 import java.io.DataInputStream;
16 import java.io.DataOutputStream;
18 import java.io.FileInputStream;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
27 import java.util.WeakHashMap;
28 import java.util.zip.ZipFile;
30 import net.sourceforge.phpdt.core.ElementChangedEvent;
31 import net.sourceforge.phpdt.core.IClasspathEntry;
32 import net.sourceforge.phpdt.core.ICompilationUnit;
33 import net.sourceforge.phpdt.core.IElementChangedListener;
34 import net.sourceforge.phpdt.core.IJavaElement;
35 import net.sourceforge.phpdt.core.IJavaElementDelta;
36 import net.sourceforge.phpdt.core.IJavaModel;
37 import net.sourceforge.phpdt.core.IJavaProject;
38 import net.sourceforge.phpdt.core.IPackageFragment;
39 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
40 import net.sourceforge.phpdt.core.IWorkingCopy;
41 import net.sourceforge.phpdt.core.JavaModelException;
42 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
43 import net.sourceforge.phpeclipse.PHPCore;
44 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
45 import net.sourceforge.phpeclipse.builder.PHPBuilder;
47 import org.eclipse.core.resources.IFile;
48 import org.eclipse.core.resources.IFolder;
49 import org.eclipse.core.resources.IProject;
50 import org.eclipse.core.resources.IResource;
51 import org.eclipse.core.resources.IResourceDelta;
52 import org.eclipse.core.resources.ISaveContext;
53 import org.eclipse.core.resources.ISaveParticipant;
54 import org.eclipse.core.resources.IWorkspace;
55 import org.eclipse.core.resources.IWorkspaceDescription;
56 import org.eclipse.core.resources.IWorkspaceRoot;
57 import org.eclipse.core.resources.ResourcesPlugin;
58 import org.eclipse.core.runtime.CoreException;
59 import org.eclipse.core.runtime.IPath;
60 import org.eclipse.core.runtime.IPluginDescriptor;
61 import org.eclipse.core.runtime.IProgressMonitor;
62 import org.eclipse.core.runtime.ISafeRunnable;
63 import org.eclipse.core.runtime.IStatus;
64 import org.eclipse.core.runtime.MultiStatus;
65 import org.eclipse.core.runtime.Path;
66 import org.eclipse.core.runtime.Platform;
67 import org.eclipse.core.runtime.Plugin;
68 import org.eclipse.core.runtime.Preferences;
69 import org.eclipse.core.runtime.Status;
72 * The <code>JavaModelManager</code> manages instances of <code>IJavaModel</code>.
73 * <code>IElementChangedListener</code>s register with the <code>JavaModelManager</code>,
74 * and receive <code>ElementChangedEvent</code>s for all <code>IJavaModel</code>s.
76 * The single instance of <code>JavaModelManager</code> is available from
77 * the static method <code>JavaModelManager.getJavaModelManager()</code>.
79 public class JavaModelManager implements ISaveParticipant {
82 * Unique handle onto the JavaModel
84 final JavaModel javaModel = new JavaModel();
87 * Classpath variables pool
89 public static HashMap Variables = new HashMap(5);
90 public static HashMap PreviousSessionVariables = new HashMap(5);
91 public static HashSet OptionNames = new HashSet(20);
92 public final static String CP_VARIABLE_PREFERENCES_PREFIX = PHPeclipsePlugin.PLUGIN_ID+".classpathVariable."; //$NON-NLS-1$
93 // public final static String CP_CONTAINER_PREFERENCES_PREFIX = PHPCore.PLUGIN_ID+".classpathContainer."; //$NON-NLS-1$
94 public final static String CP_ENTRY_IGNORE = "##<cp entry ignore>##"; //$NON-NLS-1$
97 * Classpath containers pool
99 public static HashMap Containers = new HashMap(5);
100 public static HashMap PreviousSessionContainers = new HashMap(5);
103 * Name of the extension point for contributing classpath variable initializers
105 // public static final String CPVARIABLE_INITIALIZER_EXTPOINT_ID = "classpathVariableInitializer" ; //$NON-NLS-1$
108 * Name of the extension point for contributing classpath container initializers
110 // public static final String CPCONTAINER_INITIALIZER_EXTPOINT_ID = "classpathContainerInitializer" ; //$NON-NLS-1$
113 * Name of the extension point for contributing a source code formatter
115 public static final String FORMATTER_EXTPOINT_ID = "codeFormatter" ; //$NON-NLS-1$
118 * Special value used for recognizing ongoing initialization and breaking initialization cycles
120 public final static IPath VariableInitializationInProgress = new Path("Variable Initialization In Progress"); //$NON-NLS-1$
121 // public final static IClasspathContainer ContainerInitializationInProgress = new IClasspathContainer() {
122 // public IClasspathEntry[] getClasspathEntries() { return null; }
123 // public String getDescription() { return "Container Initialization In Progress"; } //$NON-NLS-1$
124 // public int getKind() { return 0; }
125 // public IPath getPath() { return null; }
126 // public String toString() { return getDescription(); }
129 private static final String INDEX_MANAGER_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/indexmanager" ; //$NON-NLS-1$
130 private static final String COMPILER_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$
131 private static final String JAVAMODEL_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$
132 private static final String CP_RESOLVE_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/cpresolution" ; //$NON-NLS-1$
133 private static final String ZIP_ACCESS_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/zipaccess" ; //$NON-NLS-1$
134 private static final String DELTA_DEBUG =PHPeclipsePlugin.PLUGIN_ID + "/debug/javadelta" ; //$NON-NLS-1$
135 private static final String HIERARCHY_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/hierarchy" ; //$NON-NLS-1$
136 private static final String POST_ACTION_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/postaction" ; //$NON-NLS-1$
137 private static final String BUILDER_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/builder" ; //$NON-NLS-1$
138 private static final String COMPLETION_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/completion" ; //$NON-NLS-1$
139 private static final String SELECTION_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/selection" ; //$NON-NLS-1$
140 private static final String SHARED_WC_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/sharedworkingcopy" ; //$NON-NLS-1$
141 private static final String SEARCH_DEBUG = PHPeclipsePlugin.PLUGIN_ID + "/debug/search" ; //$NON-NLS-1$
143 public final static IWorkingCopy[] NoWorkingCopy = new IWorkingCopy[0];
146 * Returns whether the given full path (for a package) conflicts with the output location
147 * of the given project.
149 public static boolean conflictsWithOutputLocation(IPath folderPath, JavaProject project) {
151 IPath outputLocation = project.getOutputLocation();
152 if (outputLocation == null) {
153 // in doubt, there is a conflict
156 if (outputLocation.isPrefixOf(folderPath)) {
157 // only allow nesting in project's output if there is a corresponding source folder
158 // or if the project's output is not used (in other words, if all source folders have their custom output)
159 IClasspathEntry[] classpath = project.getResolvedClasspath(true);
160 boolean isOutputUsed = false;
161 for (int i = 0, length = classpath.length; i < length; i++) {
162 IClasspathEntry entry = classpath[i];
163 if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
164 if (entry.getPath().equals(outputLocation)) {
167 if (entry.getOutputLocation() == null) {
175 } catch (JavaModelException e) {
176 // in doubt, there is a conflict
181 // public static IClasspathContainer containerGet(IJavaProject project, IPath containerPath) {
182 // Map projectContainers = (Map)Containers.get(project);
183 // if (projectContainers == null){
186 // IClasspathContainer container = (IClasspathContainer)projectContainers.get(containerPath);
190 // public static void containerPut(IJavaProject project, IPath containerPath, IClasspathContainer container){
192 // Map projectContainers = (Map)Containers.get(project);
193 // if (projectContainers == null){
194 // projectContainers = new HashMap(1);
195 // Containers.put(project, projectContainers);
198 // if (container == null) {
199 // projectContainers.remove(containerPath);
200 // Map previousContainers = (Map)PreviousSessionContainers.get(project);
201 // if (previousContainers != null){
202 // previousContainers.remove(containerPath);
205 // projectContainers.put(containerPath, container);
208 // // do not write out intermediate initialization value
209 // if (container == JavaModelManager.ContainerInitializationInProgress) {
212 // Preferences preferences = PHPeclipsePlugin.getPlugin().getPluginPreferences();
213 // String containerKey = CP_CONTAINER_PREFERENCES_PREFIX+project.getElementName() +"|"+containerPath;//$NON-NLS-1$
214 // String containerString = CP_ENTRY_IGNORE;
216 // if (container != null) {
217 // containerString = ((JavaProject)project).encodeClasspath(container.getClasspathEntries(), null, false);
219 // } catch(JavaModelException e){
221 // preferences.setDefault(containerKey, CP_ENTRY_IGNORE); // use this default to get rid of removed ones
222 // preferences.setValue(containerKey, containerString);
223 // PHPeclipsePlugin.getPlugin().savePluginPreferences();
227 * Returns the Java element corresponding to the given resource, or
228 * <code>null</code> if unable to associate the given resource
229 * with a Java element.
231 * The resource must be one of:<ul>
232 * <li>a project - the element returned is the corresponding <code>IJavaProject</code></li>
233 * <li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li>
234 * <li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li>
235 * <li>a <code>.jar</code> file - the element returned is the corresponding <code>IPackageFragmentRoot</code></li>
236 * <li>a folder - the element returned is the corresponding <code>IPackageFragmentRoot</code>
237 * or <code>IPackageFragment</code></li>
238 * <li>the workspace root resource - the element returned is the <code>IJavaModel</code></li>
241 * Creating a Java element has the side effect of creating and opening all of the
242 * element's parents if they are not yet open.
244 public static IJavaElement create(IResource resource, IJavaProject project) {
245 if (resource == null) {
248 int type = resource.getType();
250 case IResource.PROJECT :
251 return PHPCore.create((IProject) resource);
252 case IResource.FILE :
253 return create((IFile) resource, project);
254 case IResource.FOLDER :
255 return create((IFolder) resource, project);
256 case IResource.ROOT :
257 return PHPCore.create((IWorkspaceRoot) resource);
264 * Returns the Java element corresponding to the given file, its project being the given
266 * Returns <code>null</code> if unable to associate the given file
267 * with a Java element.
269 * <p>The file must be one of:<ul>
270 * <li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li>
271 * <li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li>
272 * <li>a <code>.jar</code> file - the element returned is the corresponding <code>IPackageFragmentRoot</code></li>
275 * Creating a Java element has the side effect of creating and opening all of the
276 * element's parents if they are not yet open.
278 public static IJavaElement create(IFile file, IJavaProject project) {
282 if (project == null) {
283 project = PHPCore.create(file.getProject());
286 if (file.getFileExtension() != null) {
287 String name = file.getName();
288 // if (Util.isValidCompilationUnitName(name))
289 if (PHPFileUtil.isPHPFile(file))
290 return createCompilationUnitFrom(file, project);
291 // if (Util.isValidClassFileName(name))
292 // return createClassFileFrom(file, project);
293 // if (Util.isArchiveFileName(name))
294 // return createJarPackageFragmentRootFrom(file, project);
300 * Returns the package fragment or package fragment root corresponding to the given folder,
301 * its parent or great parent being the given project.
302 * or <code>null</code> if unable to associate the given folder with a Java element.
304 * Note that a package fragment root is returned rather than a default package.
306 * Creating a Java element has the side effect of creating and opening all of the
307 * element's parents if they are not yet open.
309 public static IJavaElement create(IFolder folder, IJavaProject project) {
310 if (folder == null) {
313 if (project == null) {
314 project = PHPCore.create(folder.getProject());
316 IJavaElement element = determineIfOnClasspath(folder, project);
317 if (conflictsWithOutputLocation(folder.getFullPath(), (JavaProject)project)
318 || (folder.getName().indexOf('.') >= 0
319 && !(element instanceof IPackageFragmentRoot))) {
320 return null; // only package fragment roots are allowed with dot names
327 * Creates and returns a class file element for the given <code>.class</code> file,
328 * its project being the given project. Returns <code>null</code> if unable
329 * to recognize the class file.
331 // public static IClassFile createClassFileFrom(IFile file, IJavaProject project ) {
332 // if (file == null) {
335 // if (project == null) {
336 // project = PHPCore.create(file.getProject());
338 // IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
339 // if (pkg == null) {
340 // // fix for 1FVS7WE
341 // // not on classpath - make the root its folder, and a default package
342 // IPackageFragmentRoot root = project.getPackageFragmentRoot(file.getParent());
343 // pkg = root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
345 // return pkg.getClassFile(file.getName());
349 * Creates and returns a compilation unit element for the given <code>.java</code>
350 * file, its project being the given project. Returns <code>null</code> if unable
351 * to recognize the compilation unit.
353 public static ICompilationUnit createCompilationUnitFrom(IFile file, IJavaProject project) {
355 if (file == null) return null;
357 if (project == null) {
358 project = PHPCore.create(file.getProject());
360 IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
362 // not on classpath - make the root its folder, and a default package
363 IPackageFragmentRoot root = project.getPackageFragmentRoot(file.getParent());
364 pkg = root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
367 System.out.println("WARNING : creating unit element outside classpath ("+ Thread.currentThread()+"): " + file.getFullPath()); //$NON-NLS-1$//$NON-NLS-2$
370 return pkg.getCompilationUnit(file.getName());
373 * Creates and returns a handle for the given JAR file, its project being the given project.
374 * The Java model associated with the JAR's project may be
375 * created as a side effect.
376 * Returns <code>null</code> if unable to create a JAR package fragment root.
377 * (for example, if the JAR file represents a non-Java resource)
379 // public static IPackageFragmentRoot createJarPackageFragmentRootFrom(IFile file, IJavaProject project) {
380 // if (file == null) {
383 // if (project == null) {
384 // project = PHPCore.create(file.getProject());
387 // // Create a jar package fragment root only if on the classpath
388 // IPath resourcePath = file.getFullPath();
390 // IClasspathEntry[] entries = ((JavaProject)project).getResolvedClasspath(true);
391 // for (int i = 0, length = entries.length; i < length; i++) {
392 // IClasspathEntry entry = entries[i];
393 // IPath rootPath = entry.getPath();
394 // if (rootPath.equals(resourcePath)) {
395 // return project.getPackageFragmentRoot(file);
398 // } catch (JavaModelException e) {
404 * Returns the package fragment root represented by the resource, or
405 * the package fragment the given resource is located in, or <code>null</code>
406 * if the given resource is not on the classpath of the given project.
408 public static IJavaElement determineIfOnClasspath(
410 IJavaProject project) {
412 IPath resourcePath = resource.getFullPath();
414 IClasspathEntry[] entries =
415 Util.isJavaFileName(resourcePath.lastSegment())
416 ? project.getRawClasspath() // JAVA file can only live inside SRC folder (on the raw path)
417 : ((JavaProject)project).getResolvedClasspath(true);
419 for (int i = 0; i < entries.length; i++) {
420 IClasspathEntry entry = entries[i];
421 if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue;
422 IPath rootPath = entry.getPath();
423 if (rootPath.equals(resourcePath)) {
424 return project.getPackageFragmentRoot(resource);
425 } else if (rootPath.isPrefixOf(resourcePath) && !Util.isExcluded(resource, ((ClasspathEntry)entry).fullExclusionPatternChars())) {
426 // given we have a resource child of the root, it cannot be a JAR pkg root
427 IPackageFragmentRoot root = ((JavaProject) project).getFolderPackageFragmentRoot(rootPath);
428 if (root == null) return null;
429 IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount());
430 if (resource.getType() == IResource.FILE) {
431 // if the resource is a file, then remove the last segment which
432 // is the file name in the package
433 pkgPath = pkgPath.removeLastSegments(1);
435 // don't check validity of package name (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=26706)
436 String pkgName = pkgPath.toString().replace('/', '.');
437 return root.getPackageFragment(pkgName);
439 String pkgName = Util.packageName(pkgPath);
440 if (pkgName == null){// || JavaConventions.validatePackageName(pkgName).getSeverity() == IStatus.ERROR) {
443 return root.getPackageFragment(pkgName);
447 } catch (JavaModelException npe) {
454 * The singleton manager
456 private final static JavaModelManager Manager= new JavaModelManager();
461 protected JavaModelCache cache = new JavaModelCache();
464 * Set of elements which are out of sync with their buffers.
466 protected Map elementsOutOfSynchWithBuffers = new HashMap(11);
469 * Turns delta firing on/off. By default it is on.
471 private boolean isFiring= true;
474 * Queue of deltas created explicily by the Java Model that
475 * have yet to be fired.
477 ArrayList javaModelDeltas= new ArrayList();
479 * Queue of reconcile deltas on working copies that have yet to be fired.
480 * This is a table form IWorkingCopy to IJavaElementDelta
482 HashMap reconcileDeltas = new HashMap();
486 * Collection of listeners for Java element deltas
488 private IElementChangedListener[] elementChangedListeners = new IElementChangedListener[5];
489 private int[] elementChangedListenerMasks = new int[5];
490 private int elementChangedListenerCount = 0;
491 public int currentChangeEventType = ElementChangedEvent.PRE_AUTO_BUILD;
492 public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with ElementChangedEvent event masks
497 * Used to convert <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s.
499 public final DeltaProcessor deltaProcessor = new DeltaProcessor(this);
501 * Used to update the JavaModel for <code>IJavaElementDelta</code>s.
503 // private final ModelUpdater modelUpdater =new ModelUpdater();
505 * Workaround for bug 15168 circular errors not reported
506 * This is a cache of the projects before any project addition/deletion has started.
508 public IJavaProject[] javaProjectsCache;
511 * Table from IProject to PerProjectInfo.
512 * NOTE: this object itself is used as a lock to synchronize creation/removal of per project infos
514 protected Map perProjectInfo = new HashMap(5);
517 * A map from ICompilationUnit to IWorkingCopy
518 * of the shared working copies.
520 public Map sharedWorkingCopies = new HashMap();
523 * A weak set of the known scopes.
525 protected WeakHashMap scopes = new WeakHashMap();
527 public static class PerProjectInfo {
528 public IProject project;
529 public Object savedState;
530 public boolean triedRead;
531 public IClasspathEntry[] classpath;
532 public IClasspathEntry[] lastResolvedClasspath;
533 public Map resolvedPathToRawEntries; // reverse map from resolved path to raw entries
534 public IPath outputLocation;
535 public Preferences preferences;
536 public PerProjectInfo(IProject project) {
538 this.triedRead = false;
539 this.savedState = null;
540 this.project = project;
543 public static boolean VERBOSE = false;
544 public static boolean CP_RESOLVE_VERBOSE = false;
545 public static boolean ZIP_ACCESS_VERBOSE = false;
548 * A cache of opened zip files per thread.
549 * (map from Thread to map of IPath to java.io.ZipFile)
550 * NOTE: this object itself is used as a lock to synchronize creation/removal of entries
552 private HashMap zipFiles = new HashMap();
556 * Update the classpath variable cache
558 public static class PluginPreferencesListener implements Preferences.IPropertyChangeListener {
560 * @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
562 public void propertyChange(Preferences.PropertyChangeEvent event) {
564 String propertyName = event.getProperty();
565 if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) {
566 String varName = propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length());
567 String newValue = (String)event.getNewValue();
568 if (newValue != null && !(newValue = newValue.trim()).equals(CP_ENTRY_IGNORE)) {
569 Variables.put(varName, new Path(newValue));
571 Variables.remove(varName);
574 // TODO khartlage temp-del
575 // if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)) {
576 // recreatePersistedContainer(propertyName, (String)event.getNewValue(), false);
582 * Line separator to use throughout the JavaModel for any source edit operation
584 public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
586 * Constructs a new JavaModelManager
588 private JavaModelManager() {
592 * @deprecated - discard once debug has converted to not using it
594 public void addElementChangedListener(IElementChangedListener listener) {
595 this.addElementChangedListener(listener, ElementChangedEvent.POST_CHANGE | ElementChangedEvent.POST_RECONCILE);
598 * addElementChangedListener method comment.
599 * Need to clone defensively the listener information, in case some listener is reacting to some notification iteration by adding/changing/removing
600 * any of the other (for example, if it deregisters itself).
602 public void addElementChangedListener(IElementChangedListener listener, int eventMask) {
603 for (int i = 0; i < this.elementChangedListenerCount; i++){
604 if (this.elementChangedListeners[i].equals(listener)){
606 // only clone the masks, since we could be in the middle of notifications and one listener decide to change
607 // any event mask of another listeners (yet not notified).
608 int cloneLength = this.elementChangedListenerMasks.length;
609 System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[cloneLength], 0, cloneLength);
610 this.elementChangedListenerMasks[i] = eventMask; // could be different
614 // 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.
616 if ((length = this.elementChangedListeners.length) == this.elementChangedListenerCount){
617 System.arraycopy(this.elementChangedListeners, 0, this.elementChangedListeners = new IElementChangedListener[length*2], 0, length);
618 System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks = new int[length*2], 0, length);
620 this.elementChangedListeners[this.elementChangedListenerCount] = listener;
621 this.elementChangedListenerMasks[this.elementChangedListenerCount] = eventMask;
622 this.elementChangedListenerCount++;
626 * Starts caching ZipFiles.
627 * Ignores if there are already clients.
629 public void cacheZipFiles() {
630 synchronized(this.zipFiles) {
631 Thread currentThread = Thread.currentThread();
632 if (this.zipFiles.get(currentThread) != null) return;
633 this.zipFiles.put(currentThread, new HashMap());
636 public void closeZipFile(ZipFile zipFile) {
637 if (zipFile == null) return;
638 synchronized(this.zipFiles) {
639 if (this.zipFiles.get(Thread.currentThread()) != null) {
640 return; // zip file will be closed by call to flushZipFiles
643 if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
644 System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] Closing ZipFile on " +zipFile.getName()); //$NON-NLS-1$ //$NON-NLS-2$
647 } catch (IOException e) {
655 * Configure the plugin with respect to option settings defined in ".options" file
657 public void configurePluginDebugOptions(){
658 if(PHPCore.getPlugin().isDebugging()){
659 // TODO khartlage temp-del
660 String option = Platform.getDebugOption(BUILDER_DEBUG);
661 // if(option != null) JavaBuilder.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
663 // option = Platform.getDebugOption(COMPILER_DEBUG);
664 // if(option != null) Compiler.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
666 // option = Platform.getDebugOption(COMPLETION_DEBUG);
667 // if(option != null) CompletionEngine.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
669 option = Platform.getDebugOption(CP_RESOLVE_DEBUG);
670 if(option != null) JavaModelManager.CP_RESOLVE_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
672 option = Platform.getDebugOption(DELTA_DEBUG);
673 if(option != null) DeltaProcessor.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
675 // option = Platform.getDebugOption(HIERARCHY_DEBUG);
676 // if(option != null) TypeHierarchy.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
678 // option = Platform.getDebugOption(INDEX_MANAGER_DEBUG);
679 // if(option != null) IndexManager.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
681 option = Platform.getDebugOption(JAVAMODEL_DEBUG);
682 if(option != null) JavaModelManager.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
684 option = Platform.getDebugOption(POST_ACTION_DEBUG);
685 if(option != null) JavaModelOperation.POST_ACTION_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
687 // option = Platform.getDebugOption(SEARCH_DEBUG);
688 // if(option != null) SearchEngine.VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
690 // option = Platform.getDebugOption(SELECTION_DEBUG);
691 // if(option != null) SelectionEngine.DEBUG = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
693 option = Platform.getDebugOption(SHARED_WC_DEBUG);
694 if(option != null) CompilationUnit.SHARED_WC_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
696 option = Platform.getDebugOption(ZIP_ACCESS_DEBUG);
697 if(option != null) JavaModelManager.ZIP_ACCESS_VERBOSE = option.equalsIgnoreCase("true") ; //$NON-NLS-1$
704 * @see ISaveParticipant
706 public void doneSaving(ISaveContext context){
710 * Fire Java Model delta, flushing them after the fact after post_change notification.
711 * If the firing mode has been turned off, this has no effect.
713 public void fire(IJavaElementDelta customDelta, int eventType) {
715 if (!this.isFiring) return;
717 if (DeltaProcessor.VERBOSE && (eventType == DEFAULT_CHANGE_EVENT || eventType == ElementChangedEvent.PRE_AUTO_BUILD)) {
718 System.out.println("-----------------------------------------------------------------------------------------------------------------------");//$NON-NLS-1$
721 IJavaElementDelta deltaToNotify;
722 if (customDelta == null){
723 deltaToNotify = this.mergeDeltas(this.javaModelDeltas);
725 deltaToNotify = customDelta;
728 // Refresh internal scopes
729 if (deltaToNotify != null) {
730 // TODO khartlage temp-del
731 // Iterator scopes = this.scopes.keySet().iterator();
732 // while (scopes.hasNext()) {
733 // AbstractSearchScope scope = (AbstractSearchScope)scopes.next();
734 // scope.processDelta(deltaToNotify);
740 // Important: if any listener reacts to notification by updating the listeners list or mask, these lists will
741 // be duplicated, so it is necessary to remember original lists in a variable (since field values may change under us)
742 IElementChangedListener[] listeners = this.elementChangedListeners;
743 int[] listenerMask = this.elementChangedListenerMasks;
744 int listenerCount = this.elementChangedListenerCount;
747 case DEFAULT_CHANGE_EVENT:
748 firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask, listenerCount);
749 firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount);
750 fireReconcileDelta(listeners, listenerMask, listenerCount);
752 case ElementChangedEvent.PRE_AUTO_BUILD:
753 firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask, listenerCount);
755 case ElementChangedEvent.POST_CHANGE:
756 firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount);
757 fireReconcileDelta(listeners, listenerMask, listenerCount);
763 private void firePreAutoBuildDelta(
764 IJavaElementDelta deltaToNotify,
765 IElementChangedListener[] listeners,
769 if (DeltaProcessor.VERBOSE){
770 System.out.println("FIRING PRE_AUTO_BUILD Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
771 System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
773 if (deltaToNotify != null) {
774 notifyListeners(deltaToNotify, ElementChangedEvent.PRE_AUTO_BUILD, listeners, listenerMask, listenerCount);
778 private void firePostChangeDelta(
779 IJavaElementDelta deltaToNotify,
780 IElementChangedListener[] listeners,
784 // post change deltas
785 if (DeltaProcessor.VERBOSE){
786 System.out.println("FIRING POST_CHANGE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
787 System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
789 if (deltaToNotify != null) {
790 // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
793 notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE, listeners, listenerMask, listenerCount);
796 private void fireReconcileDelta(
797 IElementChangedListener[] listeners,
802 IJavaElementDelta deltaToNotify = mergeDeltas(this.reconcileDeltas.values());
803 if (DeltaProcessor.VERBOSE){
804 System.out.println("FIRING POST_RECONCILE Delta ["+Thread.currentThread()+"]:"); //$NON-NLS-1$//$NON-NLS-2$
805 System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
807 if (deltaToNotify != null) {
808 // flush now so as to keep listener reactions to post their own deltas for subsequent iteration
809 this.reconcileDeltas = new HashMap();
811 notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE, listeners, listenerMask, listenerCount);
815 public void notifyListeners(IJavaElementDelta deltaToNotify, int eventType, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) {
816 final ElementChangedEvent extraEvent = new ElementChangedEvent(deltaToNotify, eventType);
817 for (int i= 0; i < listenerCount; i++) {
818 if ((listenerMask[i] & eventType) != 0){
819 final IElementChangedListener listener = listeners[i];
821 if (DeltaProcessor.VERBOSE) {
822 System.out.print("Listener #" + (i+1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$
823 start = System.currentTimeMillis();
825 // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief
826 Platform.run(new ISafeRunnable() {
827 public void handleException(Throwable exception) {
828 Util.log(exception, "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$
830 public void run() throws Exception {
831 listener.elementChanged(extraEvent);
834 if (DeltaProcessor.VERBOSE) {
835 System.out.println(" -> " + (System.currentTimeMillis()-start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
842 * Flushes all deltas without firing them.
844 protected void flush() {
845 this.javaModelDeltas = new ArrayList();
849 * Flushes ZipFiles cache if there are no more clients.
851 public void flushZipFiles() {
852 synchronized(this.zipFiles) {
853 Thread currentThread = Thread.currentThread();
854 HashMap map = (HashMap)this.zipFiles.remove(currentThread);
855 if (map == null) return;
856 Iterator iterator = map.values().iterator();
857 while (iterator.hasNext()) {
859 ZipFile zipFile = (ZipFile)iterator.next();
860 if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
861 System.out.println("(" + currentThread + ") [JavaModelManager.flushZipFiles()] Closing ZipFile on " +zipFile.getName()); //$NON-NLS-1$//$NON-NLS-2$
864 } catch (IOException e) {
873 * Returns the set of elements which are out of synch with their buffers.
875 protected Map getElementsOutOfSynchWithBuffers() {
876 return this.elementsOutOfSynchWithBuffers;
880 * Returns the <code>IJavaElement</code> represented by the
881 * <code>String</code> memento.
883 public IJavaElement getHandleFromMemento(String memento) throws JavaModelException {
884 if (memento == null) {
887 JavaModel model= (JavaModel) getJavaModel();
888 if (memento.equals("")){ // workspace memento //$NON-NLS-1$
891 int modelEnd= memento.indexOf(JavaElement.JEM_JAVAPROJECT);
892 if (modelEnd == -1) {
895 boolean returnProject= false;
896 int projectEnd= memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENTROOT, modelEnd);
897 if (projectEnd == -1) {
898 projectEnd= memento.length();
901 String projectName= memento.substring(modelEnd + 1, projectEnd);
902 JavaProject proj= (JavaProject) model.getJavaProject(projectName);
906 int rootEnd= memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENT, projectEnd + 1);
907 // TODO khartlage temp-del
908 // if (rootEnd == -1) {
909 // return model.getHandleFromMementoForRoot(memento, proj, projectEnd, memento.length());
911 // IPackageFragmentRoot root = model.getHandleFromMementoForRoot(memento, proj, projectEnd, rootEnd);
915 // int end= memento.indexOf(JavaElement.JEM_COMPILATIONUNIT, rootEnd);
917 // end= memento.indexOf(JavaElement.JEM_CLASSFILE, rootEnd);
919 // if (rootEnd + 1 == memento.length()) {
920 // return root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
922 // return root.getPackageFragment(memento.substring(rootEnd + 1));
925 // //deal with class file and binary members
926 // return model.getHandleFromMementoForBinaryMembers(memento, root, rootEnd, end);
929 // //deal with compilation units and source members
930 // return model.getHandleFromMementoForSourceMembers(memento, root, rootEnd, end);
933 // public IndexManager getIndexManager() {
934 // return this.deltaProcessor.indexManager;
938 * Returns the info for the element.
940 public Object getInfo(IJavaElement element) {
941 return this.cache.getInfo(element);
945 * Returns the handle to the active Java Model.
947 public final JavaModel getJavaModel() {
952 * Returns the singleton JavaModelManager
954 public final static JavaModelManager getJavaModelManager() {
959 * Returns the last built state for the given project, or null if there is none.
960 * Deserializes the state if necessary.
962 * For use by image builder and evaluation support only
964 public Object getLastBuiltState(IProject project, IProgressMonitor monitor) {
965 if (!JavaProject.hasJavaNature(project)) return null; // should never be requested on non-Java projects
966 PerProjectInfo info = getPerProjectInfo(project, true/*create if missing*/);
967 if (!info.triedRead) {
968 info.triedRead = true;
971 monitor.subTask(Util.bind("build.readStateProgress", project.getName())); //$NON-NLS-1$
972 info.savedState = readState(project);
973 } catch (CoreException e) {
977 return info.savedState;
981 * Returns the per-project info for the given project. If specified, create the info if the info doesn't exist.
983 public PerProjectInfo getPerProjectInfo(IProject project, boolean create) {
984 synchronized(perProjectInfo) { // use the perProjectInfo collection as its own lock
985 PerProjectInfo info= (PerProjectInfo) perProjectInfo.get(project);
986 if (info == null && create) {
987 info= new PerProjectInfo(project);
988 perProjectInfo.put(project, info);
995 * Returns the per-project info for the given project.
996 * If the info doesn't exist, check for the project existence and create the info.
997 * @throws JavaModelException if the project doesn't exist.
999 public PerProjectInfo getPerProjectInfoCheckExistence(IProject project) throws JavaModelException {
1000 JavaModelManager.PerProjectInfo info = getPerProjectInfo(project, false /* don't create info */);
1002 if (!JavaProject.hasJavaNature(project)) {
1003 throw ((JavaProject)PHPCore.create(project)).newNotPresentException();
1005 info = getPerProjectInfo(project, true /* create info */);
1011 * Returns the name of the variables for which an CP variable initializer is registered through an extension point
1013 public static String[] getRegisteredVariableNames(){
1015 Plugin jdtCorePlugin = PHPCore.getPlugin();
1016 if (jdtCorePlugin == null) return null;
1018 ArrayList variableList = new ArrayList(5);
1019 // IExtensionPoint extension = jdtCorePlugin.getDescriptor().getExtensionPoint(JavaModelManager.CPVARIABLE_INITIALIZER_EXTPOINT_ID);
1020 // if (extension != null) {
1021 // IExtension[] extensions = extension.getExtensions();
1022 // for(int i = 0; i < extensions.length; i++){
1023 // IConfigurationElement [] configElements = extensions[i].getConfigurationElements();
1024 // for(int j = 0; j < configElements.length; j++){
1025 // String varAttribute = configElements[j].getAttribute("variable"); //$NON-NLS-1$
1026 // if (varAttribute != null) variableList.add(varAttribute);
1030 String[] variableNames = new String[variableList.size()];
1031 variableList.toArray(variableNames);
1032 return variableNames;
1036 * Returns the name of the container IDs for which an CP container initializer is registered through an extension point
1038 // public static String[] getRegisteredContainerIDs(){
1040 // Plugin jdtCorePlugin = PHPCore.getPlugin();
1041 // if (jdtCorePlugin == null) return null;
1043 // ArrayList containerIDList = new ArrayList(5);
1044 // IExtensionPoint extension = jdtCorePlugin.getDescriptor().getExtensionPoint(JavaModelManager.CPCONTAINER_INITIALIZER_EXTPOINT_ID);
1045 // if (extension != null) {
1046 // IExtension[] extensions = extension.getExtensions();
1047 // for(int i = 0; i < extensions.length; i++){
1048 // IConfigurationElement [] configElements = extensions[i].getConfigurationElements();
1049 // for(int j = 0; j < configElements.length; j++){
1050 // String idAttribute = configElements[j].getAttribute("id"); //$NON-NLS-1$
1051 // if (idAttribute != null) containerIDList.add(idAttribute);
1055 // String[] containerIDs = new String[containerIDList.size()];
1056 // containerIDList.toArray(containerIDs);
1057 // return containerIDs;
1061 * Returns the File to use for saving and restoring the last built state for the given project.
1063 private File getSerializationFile(IProject project) {
1064 if (!project.exists()) return null;
1065 IPluginDescriptor descr= PHPCore.getJavaCore().getDescriptor();
1066 IPath workingLocation= project.getPluginWorkingLocation(descr);
1067 return workingLocation.append("state.dat").toFile(); //$NON-NLS-1$
1071 * Returns the open ZipFile at the given location. If the ZipFile
1072 * does not yet exist, it is created, opened, and added to the cache
1073 * of open ZipFiles. The location must be a absolute path.
1075 * @exception CoreException If unable to create/open the ZipFile
1077 public ZipFile getZipFile(IPath path) throws CoreException {
1079 synchronized(this.zipFiles) { // TODO: use PeThreadObject which does synchronization
1080 Thread currentThread = Thread.currentThread();
1083 if ((map = (HashMap)this.zipFiles.get(currentThread)) != null
1084 && (zipFile = (ZipFile)map.get(path)) != null) {
1088 String fileSystemPath= null;
1089 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
1090 IResource file = root.findMember(path);
1091 if (path.isAbsolute() && file != null) {
1092 if (file == null) { // external file
1093 fileSystemPath= path.toOSString();
1094 } else { // internal resource (not an IFile or not existing)
1096 if (file.getType() != IResource.FILE || (location = file.getLocation()) == null) {
1097 throw new CoreException(new Status(IStatus.ERROR, PHPCore.PLUGIN_ID, -1, Util.bind("file.notFound", path.toString()), null)); //$NON-NLS-1$
1099 fileSystemPath= location.toOSString();
1101 } else if (!path.isAbsolute()) {
1102 file= root.getFile(path);
1103 if (file == null || file.getType() != IResource.FILE) {
1104 throw new CoreException(new Status(IStatus.ERROR, PHPCore.PLUGIN_ID, -1, Util.bind("file.notFound", path.toString()), null)); //$NON-NLS-1$
1106 IPath location = file.getLocation();
1107 if (location == null) {
1108 throw new CoreException(new Status(IStatus.ERROR, PHPCore.PLUGIN_ID, -1, Util.bind("file.notFound", path.toString()), null)); //$NON-NLS-1$
1110 fileSystemPath= location.toOSString();
1112 fileSystemPath= path.toOSString();
1116 if (ZIP_ACCESS_VERBOSE) {
1117 System.out.println("(" + currentThread + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + fileSystemPath ); //$NON-NLS-1$ //$NON-NLS-2$
1119 zipFile = new ZipFile(fileSystemPath);
1121 map.put(path, zipFile);
1124 } catch (IOException e) {
1125 throw new CoreException(new Status(Status.ERROR, PHPCore.PLUGIN_ID, -1, Util.bind("status.IOException"), e)); //$NON-NLS-1$
1130 // public void loadVariablesAndContainers() throws CoreException {
1132 // // backward compatibility, consider persistent property
1133 // QualifiedName qName = new QualifiedName(PHPCore.PLUGIN_ID, "variables"); //$NON-NLS-1$
1134 // String xmlString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName);
1137 // if (xmlString != null){
1138 // StringReader reader = new StringReader(xmlString);
1139 // Element cpElement;
1141 // DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
1142 // cpElement = parser.parse(new InputSource(reader)).getDocumentElement();
1143 // } catch(SAXException e) {
1145 // } catch(ParserConfigurationException e){
1150 // if (cpElement == null) return;
1151 // if (!cpElement.getNodeName().equalsIgnoreCase("variables")) { //$NON-NLS-1$
1155 // NodeList list= cpElement.getChildNodes();
1156 // int length= list.getLength();
1157 // for (int i= 0; i < length; ++i) {
1158 // Node node= list.item(i);
1159 // short type= node.getNodeType();
1160 // if (type == Node.ELEMENT_NODE) {
1161 // Element element= (Element) node;
1162 // if (element.getNodeName().equalsIgnoreCase("variable")) { //$NON-NLS-1$
1164 // element.getAttribute("name"), //$NON-NLS-1$
1165 // new Path(element.getAttribute("path"))); //$NON-NLS-1$
1170 // } catch(IOException e){
1172 // if (xmlString != null){
1173 // ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName, null); // flush old one
1178 // // load variables and containers from preferences into cache
1179 // Preferences preferences = PHPCore.getPlugin().getPluginPreferences();
1181 // // only get variable from preferences not set to their default
1182 // String[] propertyNames = preferences.propertyNames();
1183 // int variablePrefixLength = CP_VARIABLE_PREFERENCES_PREFIX.length();
1184 // for (int i = 0; i < propertyNames.length; i++){
1185 // String propertyName = propertyNames[i];
1186 // if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)){
1187 // String varName = propertyName.substring(variablePrefixLength);
1188 // IPath varPath = new Path(preferences.getString(propertyName).trim());
1190 // Variables.put(varName, varPath);
1191 // PreviousSessionVariables.put(varName, varPath);
1193 // if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)){
1194 // recreatePersistedContainer(propertyName, preferences.getString(propertyName), true/*add to container values*/);
1197 // // override persisted values for variables which have a registered initializer
1198 // String[] registeredVariables = getRegisteredVariableNames();
1199 // for (int i = 0; i < registeredVariables.length; i++) {
1200 // String varName = registeredVariables[i];
1201 // Variables.put(varName, null); // reset variable, but leave its entry in the Map, so it will be part of variable names.
1203 // // override persisted values for containers which have a registered initializer
1204 // String[] registeredContainerIDs = getRegisteredContainerIDs();
1205 // for (int i = 0; i < registeredContainerIDs.length; i++) {
1206 // String containerID = registeredContainerIDs[i];
1207 // Iterator projectIterator = Containers.keySet().iterator();
1208 // while (projectIterator.hasNext()){
1209 // IJavaProject project = (IJavaProject)projectIterator.next();
1210 // Map projectContainers = (Map)Containers.get(project);
1211 // if (projectContainers != null){
1212 // Iterator containerIterator = projectContainers.keySet().iterator();
1213 // while (containerIterator.hasNext()){
1214 // IPath containerPath = (IPath)containerIterator.next();
1215 // if (containerPath.segment(0).equals(containerID)) { // registered container
1216 // projectContainers.put(containerPath, null); // reset container value, but leave entry in Map
1225 * Merged all awaiting deltas.
1227 public IJavaElementDelta mergeDeltas(Collection deltas) {
1228 if (deltas.size() == 0) return null;
1229 if (deltas.size() == 1) return (IJavaElementDelta)deltas.iterator().next();
1231 if (DeltaProcessor.VERBOSE) {
1232 System.out.println("MERGING " + deltas.size() + " DELTAS ["+Thread.currentThread()+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1235 Iterator iterator = deltas.iterator();
1236 IJavaElement javaModel = this.getJavaModel();
1237 JavaElementDelta rootDelta = new JavaElementDelta(javaModel);
1238 boolean insertedTree = false;
1239 while (iterator.hasNext()) {
1240 JavaElementDelta delta = (JavaElementDelta)iterator.next();
1241 if (DeltaProcessor.VERBOSE) {
1242 System.out.println(delta.toString());
1244 IJavaElement element = delta.getElement();
1245 if (javaModel.equals(element)) {
1246 IJavaElementDelta[] children = delta.getAffectedChildren();
1247 for (int j = 0; j < children.length; j++) {
1248 JavaElementDelta projectDelta = (JavaElementDelta) children[j];
1249 rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta);
1250 insertedTree = true;
1252 IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
1253 if (resourceDeltas != null) {
1254 for (int i = 0, length = resourceDeltas.length; i < length; i++) {
1255 rootDelta.addResourceDelta(resourceDeltas[i]);
1256 insertedTree = true;
1260 rootDelta.insertDeltaTree(element, delta);
1261 insertedTree = true;
1273 * Returns the info for this element without
1274 * disturbing the cache ordering.
1275 */ // TODO: should be synchronized, could answer unitialized info or if cache is in middle of rehash, could even answer distinct element info
1276 protected Object peekAtInfo(IJavaElement element) {
1277 return this.cache.peekAtInfo(element);
1281 * @see ISaveParticipant
1283 public void prepareToSave(ISaveContext context) throws CoreException {
1286 protected void putInfo(IJavaElement element, Object info) {
1287 this.cache.putInfo(element, info);
1291 * Reads the build state for the relevant project.
1293 protected Object readState(IProject project) throws CoreException {
1294 File file = getSerializationFile(project);
1295 if (file != null && file.exists()) {
1297 DataInputStream in= new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
1299 String pluginID= in.readUTF();
1300 if (!pluginID.equals(PHPCore.PLUGIN_ID))
1301 throw new IOException(Util.bind("build.wrongFileFormat")); //$NON-NLS-1$
1302 String kind= in.readUTF();
1303 if (!kind.equals("STATE")) //$NON-NLS-1$
1304 throw new IOException(Util.bind("build.wrongFileFormat")); //$NON-NLS-1$
1305 if (in.readBoolean())
1306 return PHPBuilder.readState(project, in);
1307 // TODO khartlage temp-del
1308 // if (JavaBuilder.DEBUG)
1309 // System.out.println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$
1313 } catch (Exception e) {
1314 e.printStackTrace();
1315 throw new CoreException(new Status(IStatus.ERROR, PHPCore.PLUGIN_ID, Platform.PLUGIN_ERROR, "Error reading last build state for project "+ project.getName(), e)); //$NON-NLS-1$
1321 // public static void recreatePersistedContainer(String propertyName, String containerString, boolean addToContainerValues) {
1322 // int containerPrefixLength = CP_CONTAINER_PREFERENCES_PREFIX.length();
1323 // int index = propertyName.indexOf('|', containerPrefixLength);
1324 // if (containerString != null) containerString = containerString.trim();
1326 // final String projectName = propertyName.substring(containerPrefixLength, index).trim();
1327 // JavaProject project = (JavaProject)getJavaModelManager().getJavaModel().getJavaProject(projectName);
1328 // final IPath containerPath = new Path(propertyName.substring(index+1).trim());
1330 // if (containerString == null || containerString.equals(CP_ENTRY_IGNORE)) {
1331 // containerPut(project, containerPath, null);
1333 // final IClasspathEntry[] containerEntries = project.decodeClasspath(containerString, false, false);
1334 // if (containerEntries != null && containerEntries != JavaProject.INVALID_CLASSPATH) {
1335 // IClasspathContainer container = new IClasspathContainer() {
1336 // public IClasspathEntry[] getClasspathEntries() {
1337 // return containerEntries;
1339 // public String getDescription() {
1340 // return "Persisted container ["+containerPath+" for project ["+ projectName+"]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
1342 // public int getKind() {
1345 // public IPath getPath() {
1346 // return containerPath;
1348 // public String toString() {
1349 // return getDescription();
1353 // if (addToContainerValues) {
1354 // containerPut(project, containerPath, container);
1356 // Map projectContainers = (Map)PreviousSessionContainers.get(project);
1357 // if (projectContainers == null){
1358 // projectContainers = new HashMap(1);
1359 // PreviousSessionContainers.put(project, projectContainers);
1361 // projectContainers.put(containerPath, container);
1368 * Registers the given delta with this manager.
1370 protected void registerJavaModelDelta(IJavaElementDelta delta) {
1371 this.javaModelDeltas.add(delta);
1375 * Remembers the given scope in a weak set
1376 * (so no need to remove it: it will be removed by the garbage collector)
1378 // public void rememberScope(AbstractSearchScope scope) {
1379 // // NB: The value has to be null so as to not create a strong reference on the scope
1380 // this.scopes.put(scope, null);
1384 * removeElementChangedListener method comment.
1386 public void removeElementChangedListener(IElementChangedListener listener) {
1388 for (int i = 0; i < this.elementChangedListenerCount; i++){
1390 if (this.elementChangedListeners[i].equals(listener)){
1392 // need to clone defensively since we might be in the middle of listener notifications (#fire)
1393 int length = this.elementChangedListeners.length;
1394 IElementChangedListener[] newListeners = new IElementChangedListener[length];
1395 System.arraycopy(this.elementChangedListeners, 0, newListeners, 0, i);
1396 int[] newMasks = new int[length];
1397 System.arraycopy(this.elementChangedListenerMasks, 0, newMasks, 0, i);
1399 // copy trailing listeners
1400 int trailingLength = this.elementChangedListenerCount - i - 1;
1401 if (trailingLength > 0){
1402 System.arraycopy(this.elementChangedListeners, i+1, newListeners, i, trailingLength);
1403 System.arraycopy(this.elementChangedListenerMasks, i+1, newMasks, i, trailingLength);
1406 // update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto
1407 // the original ones)
1408 this.elementChangedListeners = newListeners;
1409 this.elementChangedListenerMasks = newMasks;
1410 this.elementChangedListenerCount--;
1416 protected void removeInfo(IJavaElement element) {
1417 this.cache.removeInfo(element);
1420 public void removePerProjectInfo(JavaProject javaProject) {
1421 synchronized(perProjectInfo) { // use the perProjectInfo collection as its own lock
1422 IProject project = javaProject.getProject();
1423 PerProjectInfo info= (PerProjectInfo) perProjectInfo.get(project);
1425 perProjectInfo.remove(project);
1431 * @see ISaveParticipant
1433 public void rollback(ISaveContext context){
1436 private void saveState(PerProjectInfo info, ISaveContext context) throws CoreException {
1438 // passed this point, save actions are non trivial
1439 if (context.getKind() == ISaveContext.SNAPSHOT) return;
1442 if (info.triedRead) saveBuiltState(info);
1446 * Saves the built state for the project.
1448 private void saveBuiltState(PerProjectInfo info) throws CoreException {
1449 // if (JavaBuilder.DEBUG)
1450 // System.out.println(Util.bind("build.saveStateProgress", info.project.getName())); //$NON-NLS-1$
1451 File file = getSerializationFile(info.project);
1452 if (file == null) return;
1453 long t = System.currentTimeMillis();
1455 DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
1457 out.writeUTF(PHPCore.PLUGIN_ID);
1458 out.writeUTF("STATE"); //$NON-NLS-1$
1459 if (info.savedState == null) {
1460 out.writeBoolean(false);
1462 out.writeBoolean(true);
1463 PHPBuilder.writeState(info.savedState, out);
1468 } catch (RuntimeException e) {
1469 try {file.delete();} catch(SecurityException se) {}
1470 throw new CoreException(
1471 new Status(IStatus.ERROR, PHPCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
1472 Util.bind("build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
1473 } catch (IOException e) {
1474 try {file.delete();} catch(SecurityException se) {}
1475 throw new CoreException(
1476 new Status(IStatus.ERROR, PHPCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
1477 Util.bind("build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
1479 // if (JavaBuilder.DEBUG) {
1480 // t = System.currentTimeMillis() - t;
1481 // System.out.println(Util.bind("build.saveStateComplete", String.valueOf(t))); //$NON-NLS-1$
1486 * @see ISaveParticipant
1488 public void saving(ISaveContext context) throws CoreException {
1490 IProject savedProject = context.getProject();
1491 if (savedProject != null) {
1492 if (!JavaProject.hasJavaNature(savedProject)) return; // ignore
1493 PerProjectInfo info = getPerProjectInfo(savedProject, true /* create info */);
1494 saveState(info, context);
1498 ArrayList vStats= null; // lazy initialized
1499 for (Iterator iter = perProjectInfo.values().iterator(); iter.hasNext();) {
1501 PerProjectInfo info = (PerProjectInfo) iter.next();
1502 saveState(info, context);
1503 } catch (CoreException e) {
1505 vStats= new ArrayList();
1506 vStats.add(e.getStatus());
1509 if (vStats != null) {
1510 IStatus[] stats= new IStatus[vStats.size()];
1511 vStats.toArray(stats);
1512 throw new CoreException(new MultiStatus(PHPCore.PLUGIN_ID, IStatus.ERROR, stats, Util.bind("build.cannotSaveStates"), null)); //$NON-NLS-1$
1517 * Record the order in which to build the java projects (batch build). This order is based
1518 * on the projects classpath settings.
1520 protected void setBuildOrder(String[] javaBuildOrder) throws JavaModelException {
1522 // optional behaviour
1523 // possible value of index 0 is Compute
1524 if (!PHPCore.COMPUTE.equals(PHPCore.getOption(PHPCore.CORE_JAVA_BUILD_ORDER))) return; // cannot be customized at project level
1526 if (javaBuildOrder == null || javaBuildOrder.length <= 1) return;
1528 IWorkspace workspace = ResourcesPlugin.getWorkspace();
1529 IWorkspaceDescription description = workspace.getDescription();
1530 String[] wksBuildOrder = description.getBuildOrder();
1533 if (wksBuildOrder == null){
1534 newOrder = javaBuildOrder;
1536 // remove projects which are already mentionned in java builder order
1537 int javaCount = javaBuildOrder.length;
1538 HashMap newSet = new HashMap(javaCount); // create a set for fast check
1539 for (int i = 0; i < javaCount; i++){
1540 newSet.put(javaBuildOrder[i], javaBuildOrder[i]);
1543 int oldCount = wksBuildOrder.length;
1544 for (int i = 0; i < oldCount; i++){
1545 if (newSet.containsKey(wksBuildOrder[i])){
1546 wksBuildOrder[i] = null;
1550 // add Java ones first
1551 newOrder = new String[oldCount - removed + javaCount];
1552 System.arraycopy(javaBuildOrder, 0, newOrder, 0, javaCount); // java projects are built first
1554 // copy previous items in their respective order
1555 int index = javaCount;
1556 for (int i = 0; i < oldCount; i++){
1557 if (wksBuildOrder[i] != null){
1558 newOrder[index++] = wksBuildOrder[i];
1562 // commit the new build order out
1563 description.setBuildOrder(newOrder);
1565 workspace.setDescription(description);
1566 } catch(CoreException e){
1567 throw new JavaModelException(e);
1572 * Sets the last built state for the given project, or null to reset it.
1574 public void setLastBuiltState(IProject project, Object state) {
1575 if (!JavaProject.hasJavaNature(project)) return; // should never be requested on non-Java projects
1576 PerProjectInfo info = getPerProjectInfo(project, true /*create if missing*/);
1577 info.triedRead = true; // no point trying to re-read once using setter
1578 info.savedState = state;
1579 if (state == null) { // delete state file to ensure a full build happens if the workspace crashes
1581 File file = getSerializationFile(project);
1582 if (file != null && file.exists())
1584 } catch(SecurityException se) {}
1588 public void shutdown () {
1589 // TODO khartlage temp-del
1590 // if (this.deltaProcessor.indexManager != null){ // no more indexing
1591 // this.deltaProcessor.indexManager.shutdown();
1594 IJavaModel model = this.getJavaModel();
1595 if (model != null) {
1599 } catch (JavaModelException e) {
1604 * Turns the firing mode to on. That is, deltas that are/have been
1605 * registered will be fired.
1607 public void startDeltas() {
1608 this.isFiring= true;
1612 * Turns the firing mode to off. That is, deltas that are/have been
1613 * registered will not be fired until deltas are started again.
1615 public void stopDeltas() {
1616 this.isFiring= false;
1620 * Update Java Model given some delta
1622 // public void updateJavaModel(IJavaElementDelta customDelta) {
1624 // if (customDelta == null){
1625 // for (int i = 0, length = this.javaModelDeltas.size(); i < length; i++){
1626 // IJavaElementDelta delta = (IJavaElementDelta)this.javaModelDeltas.get(i);
1627 // this.modelUpdater.processJavaDelta(delta);
1630 // this.modelUpdater.processJavaDelta(customDelta);
1636 public static IPath variableGet(String variableName){
1637 return (IPath)Variables.get(variableName);
1640 public static String[] variableNames(){
1641 int length = Variables.size();
1642 String[] result = new String[length];
1643 Iterator vars = Variables.keySet().iterator();
1645 while (vars.hasNext()) {
1646 result[index++] = (String) vars.next();
1651 public static void variablePut(String variableName, IPath variablePath){
1653 // update cache - do not only rely on listener refresh
1654 if (variablePath == null) {
1655 Variables.remove(variableName);
1656 PreviousSessionVariables.remove(variableName);
1658 Variables.put(variableName, variablePath);
1661 // do not write out intermediate initialization value
1662 if (variablePath == JavaModelManager.VariableInitializationInProgress){
1665 Preferences preferences = PHPCore.getPlugin().getPluginPreferences();
1666 String variableKey = CP_VARIABLE_PREFERENCES_PREFIX+variableName;
1667 String variableString = variablePath == null ? CP_ENTRY_IGNORE : variablePath.toString();
1668 preferences.setDefault(variableKey, CP_ENTRY_IGNORE); // use this default to get rid of removed ones
1669 preferences.setValue(variableKey, variableString);
1670 PHPCore.getPlugin().savePluginPreferences();