d2ffed9ba3d3f6f4369d75cfc91bed34415afdaa
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaModelManager.java
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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
13 import java.io.BufferedInputStream;
14 import java.io.BufferedOutputStream;
15 import java.io.DataInputStream;
16 import java.io.DataOutputStream;
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.text.NumberFormat;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.WeakHashMap;
29 import java.util.zip.ZipFile;
30
31 import net.sourceforge.phpdt.core.ElementChangedEvent;
32 import net.sourceforge.phpdt.core.IClasspathEntry;
33 import net.sourceforge.phpdt.core.ICompilationUnit;
34 import net.sourceforge.phpdt.core.IElementChangedListener;
35 import net.sourceforge.phpdt.core.IJavaElement;
36 import net.sourceforge.phpdt.core.IJavaElementDelta;
37 import net.sourceforge.phpdt.core.IJavaModel;
38 import net.sourceforge.phpdt.core.IJavaProject;
39 import net.sourceforge.phpdt.core.IPackageFragment;
40 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
41 import net.sourceforge.phpdt.core.IParent;
42 import net.sourceforge.phpdt.core.IProblemRequestor;
43 import net.sourceforge.phpdt.core.IWorkingCopy;
44 import net.sourceforge.phpdt.core.JavaCore;
45 import net.sourceforge.phpdt.core.JavaModelException;
46 import net.sourceforge.phpdt.core.WorkingCopyOwner;
47 import net.sourceforge.phpdt.core.compiler.IProblem;
48 import net.sourceforge.phpdt.internal.core.builder.PHPBuilder;
49 import net.sourceforge.phpdt.internal.core.util.Util;
50 import net.sourceforge.phpdt.internal.core.util.PHPFileUtil;
51 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
52
53 import org.eclipse.core.resources.IFile;
54 import org.eclipse.core.resources.IFolder;
55 import org.eclipse.core.resources.IProject;
56 import org.eclipse.core.resources.IResource;
57 import org.eclipse.core.resources.IResourceDelta;
58 import org.eclipse.core.resources.ISaveContext;
59 import org.eclipse.core.resources.ISaveParticipant;
60 import org.eclipse.core.resources.IWorkspace;
61 import org.eclipse.core.resources.IWorkspaceDescription;
62 import org.eclipse.core.resources.IWorkspaceRoot;
63 import org.eclipse.core.resources.ResourcesPlugin;
64 import org.eclipse.core.runtime.CoreException;
65 import org.eclipse.core.runtime.IPath;
66 import org.eclipse.core.runtime.IProgressMonitor;
67 import org.eclipse.core.runtime.ISafeRunnable;
68 import org.eclipse.core.runtime.IStatus;
69 import org.eclipse.core.runtime.MultiStatus;
70 import org.eclipse.core.runtime.Path;
71 import org.eclipse.core.runtime.Platform;
72 import org.eclipse.core.runtime.Plugin;
73 import org.eclipse.core.runtime.Preferences;
74 import org.eclipse.core.runtime.SafeRunner;
75 import org.eclipse.core.runtime.Status;
76 import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
77
78 /**
79  * The <code>JavaModelManager</code> manages instances of
80  * <code>IJavaModel</code>. <code>IElementChangedListener</code>s register
81  * with the <code>JavaModelManager</code>, and receive
82  * <code>ElementChangedEvent</code>s for all <code>IJavaModel</code>s.
83  * <p>
84  * The single instance of <code>JavaModelManager</code> is available from the
85  * static method <code>JavaModelManager.getJavaModelManager()</code>.
86  */
87 public class JavaModelManager implements ISaveParticipant {
88         /**
89          * Unique handle onto the JavaModel
90          */
91         final JavaModel javaModel = new JavaModel();
92
93         // public IndexManager indexManager = new IndexManager();
94         /**
95          * Classpath variables pool
96          */
97         public static HashMap Variables = new HashMap(5);
98
99         public static HashMap PreviousSessionVariables = new HashMap(5);
100
101         public static HashSet OptionNames = new HashSet(20);
102
103         public final static String CP_VARIABLE_PREFERENCES_PREFIX = PHPeclipsePlugin.PLUGIN_ID
104                         + ".classpathVariable."; //$NON-NLS-1$
105
106         public final static String CP_CONTAINER_PREFERENCES_PREFIX = PHPeclipsePlugin.PLUGIN_ID
107                         + ".classpathContainer."; //$NON-NLS-1$
108
109         public final static String CP_ENTRY_IGNORE = "##<cp entry ignore>##"; //$NON-NLS-1$
110
111         /**
112          * Classpath containers pool
113          */
114         public static HashMap containers = new HashMap(5);
115
116         public static HashMap PreviousSessionContainers = new HashMap(5);
117
118         /**
119          * Name of the extension point for contributing classpath variable
120          * initializers
121          */
122         // public static final String CPVARIABLE_INITIALIZER_EXTPOINT_ID =
123         // "classpathVariableInitializer" ; //$NON-NLS-1$
124         /**
125          * Name of the extension point for contributing classpath container
126          * initializers
127          */
128         // public static final String CPCONTAINER_INITIALIZER_EXTPOINT_ID =
129         // "classpathContainerInitializer" ; //$NON-NLS-1$
130         /**
131          * Name of the extension point for contributing a source code formatter
132          */
133         public static final String FORMATTER_EXTPOINT_ID = "codeFormatter"; // $/**
134
135         /**
136          * Value of the content-type for Java source files
137          */
138         public static final String JAVA_SOURCE_CONTENT_TYPE = PHPeclipsePlugin.PLUGIN_ID
139                         + ".phpSource"; //$NON-NLS-1$NON-NLS-1$
140
141         /**
142          * Special value used for recognizing ongoing initialization and breaking
143          * initialization cycles
144          */
145         public final static IPath VariableInitializationInProgress = new Path(
146                         "Variable Initialization In Progress"); //$NON-NLS-1$
147         // public final static IClasspathContainer ContainerInitializationInProgress
148         // = new IClasspathContainer() {
149         // public IClasspathEntry[] getClasspathEntries() { return null; }
150         // public String getDescription() { return "Container Initialization In
151         // Progress"; } //$NON-NLS-1$
152         // public int getKind() { return 0; }
153         // public IPath getPath() { return null; }
154         // public String toString() { return getDescription(); }
155         // };
156
157         private static final String INDEX_MANAGER_DEBUG = PHPeclipsePlugin.PLUGIN_ID
158                         + "/debug/indexmanager"; //$NON-NLS-1$
159
160         private static final String COMPILER_DEBUG = PHPeclipsePlugin.PLUGIN_ID
161                         + "/debug/compiler"; //$NON-NLS-1$
162
163         private static final String JAVAMODEL_DEBUG = PHPeclipsePlugin.PLUGIN_ID
164                         + "/debug/javamodel"; //$NON-NLS-1$
165
166         private static final String CP_RESOLVE_DEBUG = PHPeclipsePlugin.PLUGIN_ID
167                         + "/debug/cpresolution"; //$NON-NLS-1$
168
169         private static final String ZIP_ACCESS_DEBUG = PHPeclipsePlugin.PLUGIN_ID
170                         + "/debug/zipaccess"; //$NON-NLS-1$
171
172         private static final String DELTA_DEBUG = PHPeclipsePlugin.PLUGIN_ID
173                         + "/debug/javadelta"; //$NON-NLS-1$
174
175         private static final String HIERARCHY_DEBUG = PHPeclipsePlugin.PLUGIN_ID
176                         + "/debug/hierarchy"; //$NON-NLS-1$
177
178         private static final String POST_ACTION_DEBUG = PHPeclipsePlugin.PLUGIN_ID
179                         + "/debug/postaction"; //$NON-NLS-1$
180
181         private static final String BUILDER_DEBUG = PHPeclipsePlugin.PLUGIN_ID
182                         + "/debug/builder"; //$NON-NLS-1$
183
184         private static final String COMPLETION_DEBUG = PHPeclipsePlugin.PLUGIN_ID
185                         + "/debug/completion"; //$NON-NLS-1$
186
187         private static final String SELECTION_DEBUG = PHPeclipsePlugin.PLUGIN_ID
188                         + "/debug/selection"; //$NON-NLS-1$
189
190         private static final String SHARED_WC_DEBUG = PHPeclipsePlugin.PLUGIN_ID
191                         + "/debug/sharedworkingcopy"; //$NON-NLS-1$
192
193         private static final String SEARCH_DEBUG = PHPeclipsePlugin.PLUGIN_ID
194                         + "/debug/search"; //$NON-NLS-1$
195
196         public final static IWorkingCopy[] NoWorkingCopy = new IWorkingCopy[0];
197
198         /**
199          * Table from WorkingCopyOwner to a table of ICompilationUnit (working copy
200          * handle) to PerWorkingCopyInfo. NOTE: this object itself is used as a lock
201          * to synchronize creation/removal of per working copy infos
202          */
203         protected Map perWorkingCopyInfos = new HashMap(5);
204
205         /**
206          * Returns whether the given full path (for a package) conflicts with the
207          * output location of the given project.
208          */
209         public static boolean conflictsWithOutputLocation(IPath folderPath,
210                         JavaProject project) {
211                 try {
212                         IPath outputLocation = project.getOutputLocation();
213                         if (outputLocation == null) {
214                                 // in doubt, there is a conflict
215                                 return true;
216                         }
217                         if (outputLocation.isPrefixOf(folderPath)) {
218                                 // only allow nesting in project's output if there is a
219                                 // corresponding source folder
220                                 // or if the project's output is not used (in other words, if
221                                 // all source folders have their custom output)
222                                 IClasspathEntry[] classpath = project
223                                                 .getResolvedClasspath(true);
224                                 boolean isOutputUsed = false;
225                                 for (int i = 0, length = classpath.length; i < length; i++) {
226                                         IClasspathEntry entry = classpath[i];
227                                         if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
228                                                 if (entry.getPath().equals(outputLocation)) {
229                                                         return false;
230                                                 }
231                                                 if (entry.getOutputLocation() == null) {
232                                                         isOutputUsed = true;
233                                                 }
234                                         }
235                                 }
236                                 return isOutputUsed;
237                         }
238                         return false;
239                 } catch (JavaModelException e) {
240                         // in doubt, there is a conflict
241                         return true;
242                 }
243         }
244
245         // public static IClasspathContainer containerGet(IJavaProject project,
246         // IPath containerPath) {
247         // Map projectContainers = (Map)Containers.get(project);
248         // if (projectContainers == null){
249         // return null;
250         // }
251         // IClasspathContainer container =
252         // (IClasspathContainer)projectContainers.get(containerPath);
253         // return container;
254         // }
255
256         // public static void containerPut(IJavaProject project, IPath
257         // containerPath, IClasspathContainer container){
258         //
259         // Map projectContainers = (Map)Containers.get(project);
260         // if (projectContainers == null){
261         // projectContainers = new HashMap(1);
262         // Containers.put(project, projectContainers);
263         // }
264         //
265         // if (container == null) {
266         // projectContainers.remove(containerPath);
267         // Map previousContainers = (Map)PreviousSessionContainers.get(project);
268         // if (previousContainers != null){
269         // previousContainers.remove(containerPath);
270         // }
271         // } else {
272         // projectContainers.put(containerPath, container);
273         // }
274         //
275         // // do not write out intermediate initialization value
276         // if (container == JavaModelManager.ContainerInitializationInProgress) {
277         // return;
278         // }
279         // Preferences preferences =
280         // PHPeclipsePlugin.getPlugin().getPluginPreferences();
281         // String containerKey =
282         // CP_CONTAINER_PREFERENCES_PREFIX+project.getElementName()
283         // +"|"+containerPath;//$NON-NLS-1$
284         // String containerString = CP_ENTRY_IGNORE;
285         // try {
286         // if (container != null) {
287         // containerString =
288         // ((JavaProject)project).encodeClasspath(container.getClasspathEntries(),
289         // null, false);
290         // }
291         // } catch(JavaModelException e){
292         // }
293         // preferences.setDefault(containerKey, CP_ENTRY_IGNORE); // use this
294         // default to get rid of removed ones
295         // preferences.setValue(containerKey, containerString);
296         // PHPeclipsePlugin.getPlugin().savePluginPreferences();
297         // }
298
299         /**
300          * Returns the Java element corresponding to the given resource, or
301          * <code>null</code> if unable to associate the given resource with a Java
302          * element.
303          * <p>
304          * The resource must be one of:
305          * <ul>
306          * <li>a project - the element returned is the corresponding
307          * <code>IJavaProject</code></li>
308          * <li>a <code>.java</code> file - the element returned is the
309          * corresponding <code>ICompilationUnit</code></li>
310          * <li>a <code>.class</code> file - the element returned is the
311          * corresponding <code>IClassFile</code></li>
312          * <li>a <code>.jar</code> file - the element returned is the
313          * corresponding <code>IPackageFragmentRoot</code></li>
314          * <li>a folder - the element returned is the corresponding
315          * <code>IPackageFragmentRoot</code> or <code>IPackageFragment</code></li>
316          * <li>the workspace root resource - the element returned is the
317          * <code>IJavaModel</code></li>
318          * </ul>
319          * <p>
320          * Creating a Java element has the side effect of creating and opening all
321          * of the element's parents if they are not yet open.
322          */
323         public static IJavaElement create(IResource resource, IJavaProject project) {
324                 if (resource == null) {
325                         return null;
326                 }
327                 int type = resource.getType();
328                 switch (type) {
329                 case IResource.PROJECT:
330                         return JavaCore.create((IProject) resource);
331                 case IResource.FILE:
332                         return create((IFile) resource, project);
333                 case IResource.FOLDER:
334                         return create((IFolder) resource, project);
335                 case IResource.ROOT:
336                         return JavaCore.create((IWorkspaceRoot) resource);
337                 default:
338                         return null;
339                 }
340         }
341
342         /**
343          * Returns the Java element corresponding to the given file, its project
344          * being the given project. Returns <code>null</code> if unable to
345          * associate the given file with a Java element.
346          * 
347          * <p>
348          * The file must be one of:
349          * <ul>
350          * <li>a <code>.java</code> file - the element returned is the
351          * corresponding <code>ICompilationUnit</code></li>
352          * <li>a <code>.class</code> file - the element returned is the
353          * corresponding <code>IClassFile</code></li>
354          * <li>a <code>.jar</code> file - the element returned is the
355          * corresponding <code>IPackageFragmentRoot</code></li>
356          * </ul>
357          * <p>
358          * Creating a Java element has the side effect of creating and opening all
359          * of the element's parents if they are not yet open.
360          */
361         public static IJavaElement create(IFile file, IJavaProject project) {
362                 if (file == null) {
363                         return null;
364                 }
365                 if (project == null) {
366                         project = JavaCore.create(file.getProject());
367                 }
368
369                 if (file.getFileExtension() != null) {
370                         String name = file.getName();
371                         if (PHPFileUtil.isValidPHPUnitName(name))
372                                 // if (PHPFileUtil.isPHPFile(file))
373                                 return createCompilationUnitFrom(file, project);
374                         // if (ProjectPrefUtil.isValidClassFileName(name))
375                         // return createClassFileFrom(file, project);
376                         // if (ProjectPrefUtil.isArchiveFileName(name))
377                         // return createJarPackageFragmentRootFrom(file, project);
378                 }
379                 return null;
380         }
381
382         /**
383          * Returns the package fragment or package fragment root corresponding to
384          * the given folder, its parent or great parent being the given project. or
385          * <code>null</code> if unable to associate the given folder with a Java
386          * element.
387          * <p>
388          * Note that a package fragment root is returned rather than a default
389          * package.
390          * <p>
391          * Creating a Java element has the side effect of creating and opening all
392          * of the element's parents if they are not yet open.
393          */
394         public static IJavaElement create(IFolder folder, IJavaProject project) {
395                 if (folder == null) {
396                         return null;
397                 }
398                 if (project == null) {
399                         project = JavaCore.create(folder.getProject());
400                 }
401                 IJavaElement element = determineIfOnClasspath(folder, project);
402                 if (conflictsWithOutputLocation(folder.getFullPath(),
403                                 (JavaProject) project)
404                                 || (folder.getName().indexOf('.') >= 0 && !(element instanceof IPackageFragmentRoot))) {
405                         return null; // only package fragment roots are allowed with dot
406                                                         // names
407                 } else {
408                         return element;
409                 }
410         }
411
412         /**
413          * Creates and returns a class file element for the given
414          * <code>.class</code> file, its project being the given project. Returns
415          * <code>null</code> if unable to recognize the class file.
416          */
417         // public static IClassFile createClassFileFrom(IFile file, IJavaProject
418         // project ) {
419         // if (file == null) {
420         // return null;
421         // }
422         // if (project == null) {
423         // project = PHPCore.create(file.getProject());
424         // }
425         // IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file,
426         // project);
427         // if (pkg == null) {
428         // // fix for 1FVS7WE
429         // // not on classpath - make the root its folder, and a default package
430         // IPackageFragmentRoot root =
431         // project.getPackageFragmentRoot(file.getParent());
432         // pkg = root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
433         // }
434         // return pkg.getClassFile(file.getName());
435         // }
436         /**
437          * Creates and returns a compilation unit element for the given
438          * <code>.java</code> file, its project being the given project. Returns
439          * <code>null</code> if unable to recognize the compilation unit.
440          */
441         public static ICompilationUnit createCompilationUnitFrom(IFile file,
442                         IJavaProject project) {
443
444                 if (file == null)
445                         return null;
446
447                 if (project == null) {
448                         project = JavaCore.create(file.getProject());
449                 }
450                 IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file,
451                                 project);
452                 if (pkg == null) {
453                         // not on classpath - make the root its folder, and a default
454                         // package
455                         IPackageFragmentRoot root = project.getPackageFragmentRoot(file
456                                         .getParent());
457                         pkg = root
458                                         .getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
459
460                         if (VERBOSE) {
461                                 System.out
462                                                 .println("WARNING : creating unit element outside classpath (" + Thread.currentThread() + "): " + file.getFullPath()); //$NON-NLS-1$//$NON-NLS-2$
463                         }
464                 }
465                 return pkg.getCompilationUnit(file.getName());
466         }
467
468         /**
469          * Creates and returns a handle for the given JAR file, its project being
470          * the given project. The Java model associated with the JAR's project may
471          * be created as a side effect. Returns <code>null</code> if unable to
472          * create a JAR package fragment root. (for example, if the JAR file
473          * represents a non-Java resource)
474          */
475         // public static IPackageFragmentRoot createJarPackageFragmentRootFrom(IFile
476         // file, IJavaProject project) {
477         // if (file == null) {
478         // return null;
479         // }
480         // if (project == null) {
481         // project = PHPCore.create(file.getProject());
482         // }
483         //
484         // // Create a jar package fragment root only if on the classpath
485         // IPath resourcePath = file.getFullPath();
486         // try {
487         // IClasspathEntry[] entries =
488         // ((JavaProject)project).getResolvedClasspath(true);
489         // for (int i = 0, length = entries.length; i < length; i++) {
490         // IClasspathEntry entry = entries[i];
491         // IPath rootPath = entry.getPath();
492         // if (rootPath.equals(resourcePath)) {
493         // return project.getPackageFragmentRoot(file);
494         // }
495         // }
496         // } catch (JavaModelException e) {
497         // }
498         // return null;
499         // }
500         /**
501          * Returns the package fragment root represented by the resource, or the
502          * package fragment the given resource is located in, or <code>null</code>
503          * if the given resource is not on the classpath of the given project.
504          */
505         public static IJavaElement determineIfOnClasspath(IResource resource,
506                         IJavaProject project) {
507
508                 IPath resourcePath = resource.getFullPath();
509                 try {
510                         IClasspathEntry[] entries = net.sourceforge.phpdt.internal.compiler.util.Util
511                                         .isJavaFileName(resourcePath.lastSegment()) ? project
512                                         .getRawClasspath() // JAVA file can only live inside SRC
513                                                                                 // folder (on the raw path)
514                                         : ((JavaProject) project).getResolvedClasspath(true);
515
516                         for (int i = 0; i < entries.length; i++) {
517                                 IClasspathEntry entry = entries[i];
518                                 if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT)
519                                         continue;
520                                 IPath rootPath = entry.getPath();
521                                 if (rootPath.equals(resourcePath)) {
522                                         return project.getPackageFragmentRoot(resource);
523                                 } else if (rootPath.isPrefixOf(resourcePath)
524                                                 && !Util.isExcluded(resource, null,
525                                                                 ((ClasspathEntry) entry)
526                                                                                 .fullExclusionPatternChars())) {
527                                         // given we have a resource child of the root, it cannot be
528                                         // a JAR pkg root
529                                         IPackageFragmentRoot root = ((JavaProject) project)
530                                                         .getFolderPackageFragmentRoot(rootPath);
531                                         if (root == null)
532                                                 return null;
533                                         IPath pkgPath = resourcePath.removeFirstSegments(rootPath
534                                                         .segmentCount());
535                                         if (resource.getType() == IResource.FILE) {
536                                                 // if the resource is a file, then remove the last
537                                                 // segment which
538                                                 // is the file name in the package
539                                                 pkgPath = pkgPath.removeLastSegments(1);
540
541                                                 // don't check validity of package name (see
542                                                 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=26706)
543                                                 // String pkgName = pkgPath.toString().replace('/',
544                                                 // '.');
545                                                 String pkgName = pkgPath.toString();
546                                                 return root.getPackageFragment(pkgName);
547                                         } else {
548                                                 String pkgName = Util.packageName(pkgPath);
549                                                 if (pkgName == null) {// ||
550                                                                                                 // JavaConventions.validatePackageName(pkgName).getSeverity()
551                                                                                                 // == IStatus.ERROR) {
552                                                         return null;
553                                                 }
554                                                 return root.getPackageFragment(pkgName);
555                                         }
556                                 }
557                         }
558                 } catch (JavaModelException npe) {
559                         return null;
560                 }
561                 return null;
562         }
563
564         /**
565          * The singleton manager
566          */
567         private final static JavaModelManager Manager = new JavaModelManager();
568
569         /**
570          * Infos cache.
571          */
572         protected JavaModelCache cache = new JavaModelCache();
573
574         /*
575          * Temporary cache of newly opened elements
576          */
577         private ThreadLocal temporaryCache = new ThreadLocal();
578
579         /**
580          * Set of elements which are out of sync with their buffers.
581          */
582         protected Map elementsOutOfSynchWithBuffers = new HashMap(11);
583
584         /**
585          * Holds the state used for delta processing.
586          */
587         public DeltaProcessingState deltaState = new DeltaProcessingState();
588
589         /**
590          * Turns delta firing on/off. By default it is on.
591          */
592         private boolean isFiring = true;
593
594         /**
595          * Queue of deltas created explicily by the Java Model that have yet to be
596          * fired.
597          */
598         ArrayList javaModelDeltas = new ArrayList();
599
600         /**
601          * Queue of reconcile deltas on working copies that have yet to be fired.
602          * This is a table form IWorkingCopy to IJavaElementDelta
603          */
604         HashMap reconcileDeltas = new HashMap();
605
606         /**
607          * Collection of listeners for Java element deltas
608          */
609         private IElementChangedListener[] elementChangedListeners = new IElementChangedListener[5];
610
611         private int[] elementChangedListenerMasks = new int[5];
612
613         private int elementChangedListenerCount = 0;
614
615         public int currentChangeEventType = ElementChangedEvent.PRE_AUTO_BUILD;
616
617         public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with
618                                                                                                                 // ElementChangedEvent
619                                                                                                                 // event masks
620
621         /**
622          * Used to convert <code>IResourceDelta</code>s into
623          * <code>IJavaElementDelta</code>s.
624          */
625         // public final DeltaProcessor deltaProcessor = new DeltaProcessor(this);
626         /**
627          * Used to update the JavaModel for <code>IJavaElementDelta</code>s.
628          */
629         private final ModelUpdater modelUpdater = new ModelUpdater();
630
631         /**
632          * Workaround for bug 15168 circular errors not reported This is a cache of
633          * the projects before any project addition/deletion has started.
634          */
635         public IJavaProject[] javaProjectsCache;
636
637         /**
638          * Table from IProject to PerProjectInfo. NOTE: this object itself is used
639          * as a lock to synchronize creation/removal of per project infos
640          */
641         protected Map perProjectInfo = new HashMap(5);
642
643         /**
644          * A map from ICompilationUnit to IWorkingCopy of the shared working copies.
645          */
646         public Map sharedWorkingCopies = new HashMap();
647
648         /**
649          * A weak set of the known scopes.
650          */
651         protected WeakHashMap searchScopes = new WeakHashMap();
652
653         // public static class PerProjectInfo {
654         // public IProject project;
655         // public Object savedState;
656         // public boolean triedRead;
657         // public IClasspathEntry[] classpath;
658         // public IClasspathEntry[] lastResolvedClasspath;
659         // public Map resolvedPathToRawEntries; // reverse map from resolved path to
660         // raw entries
661         // public IPath outputLocation;
662         // public Preferences preferences;
663         // public PerProjectInfo(IProject project) {
664         //
665         // this.triedRead = false;
666         // this.savedState = null;
667         // this.project = project;
668         // }
669         // }
670
671         public static class PerProjectInfo {
672
673                 public IProject project;
674
675                 public Object savedState;
676
677                 public boolean triedRead;
678
679                 public IClasspathEntry[] rawClasspath;
680
681                 public IClasspathEntry[] resolvedClasspath;
682
683                 public Map resolvedPathToRawEntries; // reverse map from resolved
684                                                                                                 // path to raw entries
685
686                 public IPath outputLocation;
687
688                 public Preferences preferences;
689
690                 public PerProjectInfo(IProject project) {
691
692                         this.triedRead = false;
693                         this.savedState = null;
694                         this.project = project;
695                 }
696
697                 // updating raw classpath need to flush obsoleted cached information
698                 // about resolved entries
699                 public synchronized void updateClasspathInformation(
700                                 IClasspathEntry[] newRawClasspath) {
701
702                         this.rawClasspath = newRawClasspath;
703                         this.resolvedClasspath = null;
704                         this.resolvedPathToRawEntries = null;
705                 }
706
707                 public String toString() {
708                         StringBuffer buffer = new StringBuffer();
709                         buffer.append("Info for "); //$NON-NLS-1$
710                         buffer.append(this.project.getFullPath());
711                         buffer.append("\nRaw classpath:\n"); //$NON-NLS-1$
712                         if (this.rawClasspath == null) {
713                                 buffer.append("  <null>\n"); //$NON-NLS-1$
714                         } else {
715                                 for (int i = 0, length = this.rawClasspath.length; i < length; i++) {
716                                         buffer.append("  "); //$NON-NLS-1$
717                                         buffer.append(this.rawClasspath[i]);
718                                         buffer.append('\n');
719                                 }
720                         }
721                         buffer.append("Resolved classpath:\n"); //$NON-NLS-1$
722                         IClasspathEntry[] resolvedCP = this.resolvedClasspath;
723                         if (resolvedCP == null) {
724                                 buffer.append("  <null>\n"); //$NON-NLS-1$
725                         } else {
726                                 for (int i = 0, length = resolvedCP.length; i < length; i++) {
727                                         buffer.append("  "); //$NON-NLS-1$
728                                         buffer.append(resolvedCP[i]);
729                                         buffer.append('\n');
730                                 }
731                         }
732                         buffer.append("Output location:\n  "); //$NON-NLS-1$
733                         if (this.outputLocation == null) {
734                                 buffer.append("<null>"); //$NON-NLS-1$
735                         } else {
736                                 buffer.append(this.outputLocation);
737                         }
738                         return buffer.toString();
739                 }
740         }
741
742         public static class PerWorkingCopyInfo implements IProblemRequestor {
743                 int useCount = 0;
744
745                 IProblemRequestor problemRequestor;
746
747                 ICompilationUnit workingCopy;
748
749                 public PerWorkingCopyInfo(ICompilationUnit workingCopy,
750                                 IProblemRequestor problemRequestor) {
751                         this.workingCopy = workingCopy;
752                         this.problemRequestor = problemRequestor;
753                 }
754
755                 public void acceptProblem(IProblem problem) {
756                         if (this.problemRequestor == null)
757                                 return;
758                         this.problemRequestor.acceptProblem(problem);
759                 }
760
761                 public void beginReporting() {
762                         if (this.problemRequestor == null)
763                                 return;
764                         this.problemRequestor.beginReporting();
765                 }
766
767                 public void endReporting() {
768                         if (this.problemRequestor == null)
769                                 return;
770                         this.problemRequestor.endReporting();
771                 }
772
773                 public ICompilationUnit getWorkingCopy() {
774                         return this.workingCopy;
775                 }
776
777                 public boolean isActive() {
778                         return this.problemRequestor != null
779                                         && this.problemRequestor.isActive();
780                 }
781
782                 public String toString() {
783                         StringBuffer buffer = new StringBuffer();
784                         buffer.append("Info for "); //$NON-NLS-1$
785                         buffer.append(((JavaElement) workingCopy).toStringWithAncestors());
786                         buffer.append("\nUse count = "); //$NON-NLS-1$
787                         buffer.append(this.useCount);
788                         buffer.append("\nProblem requestor:\n  "); //$NON-NLS-1$
789                         buffer.append(this.problemRequestor);
790                         return buffer.toString();
791                 }
792         }
793
794         public static boolean VERBOSE = false;
795
796         public static boolean CP_RESOLVE_VERBOSE = false;
797
798         public static boolean ZIP_ACCESS_VERBOSE = false;
799
800         /**
801          * A cache of opened zip files per thread. (map from Thread to map of IPath
802          * to java.io.ZipFile) NOTE: this object itself is used as a lock to
803          * synchronize creation/removal of entries
804          */
805         private HashMap zipFiles = new HashMap();
806
807         /**
808          * Update the classpath variable cache
809          */
810         public static class PluginPreferencesListener implements
811                         Preferences.IPropertyChangeListener {
812                 /**
813                  * @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
814                  */
815                 public void propertyChange(Preferences.PropertyChangeEvent event) {
816                         // TODO : jsurfer temp-del
817                         // String propertyName = event.getProperty();
818                         // if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) {
819                         // String varName =
820                         // propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length());
821                         // String newValue = (String)event.getNewValue();
822                         // if (newValue != null && !(newValue =
823                         // newValue.trim()).equals(CP_ENTRY_IGNORE)) {
824                         // Variables.put(varName, new Path(newValue));
825                         // } else {
826                         // Variables.remove(varName);
827                         // }
828                         // }
829                         // if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)) {
830                         // recreatePersistedContainer(propertyName,
831                         // (String)event.getNewValue(), false);
832                         // }
833                 }
834         }
835
836         /**
837          * Line separator to use throughout the JavaModel for any source edit
838          * operation
839          */
840         public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
841
842         /**
843          * Constructs a new JavaModelManager
844          */
845         private JavaModelManager() {
846         }
847
848         /**
849          * @deprecated - discard once debug has converted to not using it
850          */
851         public void addElementChangedListener(IElementChangedListener listener) {
852                 this.addElementChangedListener(listener,
853                                 ElementChangedEvent.POST_CHANGE
854                                                 | ElementChangedEvent.POST_RECONCILE);
855         }
856
857         /**
858          * addElementChangedListener method comment. Need to clone defensively the
859          * listener information, in case some listener is reacting to some
860          * notification iteration by adding/changing/removing any of the other (for
861          * example, if it deregisters itself).
862          */
863         public void addElementChangedListener(IElementChangedListener listener,
864                         int eventMask) {
865                 for (int i = 0; i < this.elementChangedListenerCount; i++) {
866                         if (this.elementChangedListeners[i].equals(listener)) {
867
868                                 // only clone the masks, since we could be in the middle of
869                                 // notifications and one listener decide to change
870                                 // any event mask of another listeners (yet not notified).
871                                 int cloneLength = this.elementChangedListenerMasks.length;
872                                 System
873                                                 .arraycopy(
874                                                                 this.elementChangedListenerMasks,
875                                                                 0,
876                                                                 this.elementChangedListenerMasks = new int[cloneLength],
877                                                                 0, cloneLength);
878                                 this.elementChangedListenerMasks[i] = eventMask; // could be
879                                                                                                                                         // different
880                                 return;
881                         }
882                 }
883                 // may need to grow, no need to clone, since iterators will have cached
884                 // original arrays and max boundary and we only add to the end.
885                 int length;
886                 if ((length = this.elementChangedListeners.length) == this.elementChangedListenerCount) {
887                         System
888                                         .arraycopy(
889                                                         this.elementChangedListeners,
890                                                         0,
891                                                         this.elementChangedListeners = new IElementChangedListener[length * 2],
892                                                         0, length);
893                         System.arraycopy(this.elementChangedListenerMasks, 0,
894                                         this.elementChangedListenerMasks = new int[length * 2], 0,
895                                         length);
896                 }
897                 this.elementChangedListeners[this.elementChangedListenerCount] = listener;
898                 this.elementChangedListenerMasks[this.elementChangedListenerCount] = eventMask;
899                 this.elementChangedListenerCount++;
900         }
901
902         /**
903          * Starts caching ZipFiles. Ignores if there are already clients.
904          */
905         public void cacheZipFiles() {
906                 synchronized (this.zipFiles) {
907                         Thread currentThread = Thread.currentThread();
908                         if (this.zipFiles.get(currentThread) != null)
909                                 return;
910                         this.zipFiles.put(currentThread, new HashMap());
911                 }
912         }
913
914         public void closeZipFile(ZipFile zipFile) {
915                 if (zipFile == null)
916                         return;
917                 synchronized (this.zipFiles) {
918                         if (this.zipFiles.get(Thread.currentThread()) != null) {
919                                 return; // zip file will be closed by call to flushZipFiles
920                         }
921                         try {
922                                 if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
923                                         System.out
924                                                         .println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] Closing ZipFile on " + zipFile.getName()); //$NON-NLS-1$    //$NON-NLS-2$
925                                 }
926                                 zipFile.close();
927                         } catch (IOException e) {
928                         }
929                 }
930         }
931
932         /**
933          * Configure the plugin with respect to option settings defined in
934          * ".options" file
935          */
936         public void configurePluginDebugOptions() {
937                 if (JavaCore.getPlugin().isDebugging()) {
938                         // TODO jsurfer temp-del
939
940                         String option = Platform.getDebugOption(BUILDER_DEBUG);
941                         // if(option != null) JavaBuilder.DEBUG =
942                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
943                         //
944                         // option = Platform.getDebugOption(COMPILER_DEBUG);
945                         // if(option != null) Compiler.DEBUG =
946                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
947                         //
948                         // option = Platform.getDebugOption(COMPLETION_DEBUG);
949                         // if(option != null) CompletionEngine.DEBUG =
950                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
951                         //
952                         option = Platform.getDebugOption(CP_RESOLVE_DEBUG);
953                         if (option != null)
954                                 JavaModelManager.CP_RESOLVE_VERBOSE = option
955                                                 .equalsIgnoreCase("true"); //$NON-NLS-1$
956
957                         option = Platform.getDebugOption(DELTA_DEBUG);
958                         if (option != null)
959                                 DeltaProcessor.VERBOSE = option.equalsIgnoreCase("true"); //$NON-NLS-1$
960
961                         // option = Platform.getDebugOption(HIERARCHY_DEBUG);
962                         // if(option != null) TypeHierarchy.DEBUG =
963                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
964                         //
965                         // option = Platform.getDebugOption(INDEX_MANAGER_DEBUG);
966                         // if(option != null) IndexManager.VERBOSE =
967                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
968
969                         option = Platform.getDebugOption(JAVAMODEL_DEBUG);
970                         if (option != null)
971                                 JavaModelManager.VERBOSE = option.equalsIgnoreCase("true"); //$NON-NLS-1$
972
973                         option = Platform.getDebugOption(POST_ACTION_DEBUG);
974                         if (option != null)
975                                 JavaModelOperation.POST_ACTION_VERBOSE = option
976                                                 .equalsIgnoreCase("true"); //$NON-NLS-1$
977
978                         // option = Platform.getDebugOption(SEARCH_DEBUG);
979                         // if(option != null) SearchEngine.VERBOSE =
980                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
981                         //
982                         // option = Platform.getDebugOption(SELECTION_DEBUG);
983                         // if(option != null) SelectionEngine.DEBUG =
984                         // option.equalsIgnoreCase("true") ; //$NON-NLS-1$
985
986                         option = Platform.getDebugOption(ZIP_ACCESS_DEBUG);
987                         if (option != null)
988                                 JavaModelManager.ZIP_ACCESS_VERBOSE = option
989                                                 .equalsIgnoreCase("true"); //$NON-NLS-1$
990                 }
991         }
992
993         /*
994          * Discards the per working copy info for the given working copy (making it
995          * a compilation unit) if its use count was 1. Otherwise, just decrement the
996          * use count. If the working copy is primary, computes the delta between its
997          * state and the original compilation unit and register it. Close the
998          * working copy, its buffer and remove it from the shared working copy
999          * table. Ignore if no per-working copy info existed. NOTE: it must be
1000          * synchronized as it may interact with the element info cache (if useCount
1001          * is decremented to 0), see bug 50667. Returns the new use count (or -1 if
1002          * it didn't exist).
1003          */
1004         public synchronized int discardPerWorkingCopyInfo(
1005                         CompilationUnit workingCopy) throws JavaModelException {
1006                 synchronized (perWorkingCopyInfos) {
1007                         WorkingCopyOwner owner = workingCopy.owner;
1008                         Map workingCopyToInfos = (Map) this.perWorkingCopyInfos.get(owner);
1009                         if (workingCopyToInfos == null)
1010                                 return -1;
1011
1012                         PerWorkingCopyInfo info = (PerWorkingCopyInfo) workingCopyToInfos
1013                                         .get(workingCopy);
1014                         if (info == null)
1015                                 return -1;
1016
1017                         if (--info.useCount == 0) {
1018                                 // create the delta builder (this remembers the current content
1019                                 // of the working copy)
1020                                 JavaElementDeltaBuilder deltaBuilder = null;
1021                                 if (workingCopy.isPrimary()) {
1022                                         deltaBuilder = new JavaElementDeltaBuilder(workingCopy);
1023                                 }
1024
1025                                 // remove per working copy info
1026                                 workingCopyToInfos.remove(workingCopy);
1027                                 if (workingCopyToInfos.isEmpty()) {
1028                                         this.perWorkingCopyInfos.remove(owner);
1029                                 }
1030
1031                                 // remove infos + close buffer (since no longer working copy)
1032                                 removeInfoAndChildren(workingCopy);
1033                                 workingCopy.closeBuffer();
1034
1035                                 // compute the delta if needed and register it if there are
1036                                 // changes
1037                                 if (deltaBuilder != null) {
1038                                         deltaBuilder.buildDeltas();
1039                                         if ((deltaBuilder.delta != null)
1040                                                         && (deltaBuilder.delta.getAffectedChildren().length > 0)) {
1041                                                 getDeltaProcessor().registerJavaModelDelta(
1042                                                                 deltaBuilder.delta);
1043                                         }
1044                                 }
1045
1046                         }
1047                         return info.useCount;
1048                 }
1049         }
1050
1051         /**
1052          * @see ISaveParticipant
1053          */
1054         public void doneSaving(ISaveContext context) {
1055         }
1056
1057         /**
1058          * Fire Java Model delta, flushing them after the fact after post_change
1059          * notification. If the firing mode has been turned off, this has no effect.
1060          */
1061         public void fire(IJavaElementDelta customDelta, int eventType) {
1062
1063                 if (!this.isFiring)
1064                         return;
1065
1066                 if (DeltaProcessor.VERBOSE
1067                                 && (eventType == DEFAULT_CHANGE_EVENT || eventType == ElementChangedEvent.PRE_AUTO_BUILD)) {
1068                         System.out
1069                                         .println("-----------------------------------------------------------------------------------------------------------------------");//$NON-NLS-1$
1070                 }
1071
1072                 IJavaElementDelta deltaToNotify;
1073                 if (customDelta == null) {
1074                         deltaToNotify = this.mergeDeltas(this.javaModelDeltas);
1075                 } else {
1076                         deltaToNotify = customDelta;
1077                 }
1078
1079                 // Refresh internal scopes
1080                 if (deltaToNotify != null) {
1081                         // TODO temp-del
1082                         // Iterator scopes = this.scopes.keySet().iterator();
1083                         // while (scopes.hasNext()) {
1084                         // AbstractSearchScope scope = (AbstractSearchScope)scopes.next();
1085                         // scope.processDelta(deltaToNotify);
1086                         // }
1087                 }
1088
1089                 // Notification
1090
1091                 // Important: if any listener reacts to notification by updating the
1092                 // listeners list or mask, these lists will
1093                 // be duplicated, so it is necessary to remember original lists in a
1094                 // variable (since field values may change under us)
1095                 IElementChangedListener[] listeners = this.elementChangedListeners;
1096                 int[] listenerMask = this.elementChangedListenerMasks;
1097                 int listenerCount = this.elementChangedListenerCount;
1098
1099                 switch (eventType) {
1100                 case DEFAULT_CHANGE_EVENT:
1101                         firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask,
1102                                         listenerCount);
1103                         firePostChangeDelta(deltaToNotify, listeners, listenerMask,
1104                                         listenerCount);
1105                         fireReconcileDelta(listeners, listenerMask, listenerCount);
1106                         break;
1107                 case ElementChangedEvent.PRE_AUTO_BUILD:
1108                         firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask,
1109                                         listenerCount);
1110                         break;
1111                 case ElementChangedEvent.POST_CHANGE:
1112                         firePostChangeDelta(deltaToNotify, listeners, listenerMask,
1113                                         listenerCount);
1114                         fireReconcileDelta(listeners, listenerMask, listenerCount);
1115                         break;
1116                 }
1117
1118         }
1119
1120         private void firePreAutoBuildDelta(IJavaElementDelta deltaToNotify,
1121                         IElementChangedListener[] listeners, int[] listenerMask,
1122                         int listenerCount) {
1123
1124                 if (DeltaProcessor.VERBOSE) {
1125                         System.out
1126                                         .println("FIRING PRE_AUTO_BUILD Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
1127                         System.out
1128                                         .println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
1129                 }
1130                 if (deltaToNotify != null) {
1131                         notifyListeners(deltaToNotify, ElementChangedEvent.PRE_AUTO_BUILD,
1132                                         listeners, listenerMask, listenerCount);
1133                 }
1134         }
1135
1136         private void firePostChangeDelta(IJavaElementDelta deltaToNotify,
1137                         IElementChangedListener[] listeners, int[] listenerMask,
1138                         int listenerCount) {
1139
1140                 // post change deltas
1141                 if (DeltaProcessor.VERBOSE) {
1142                         System.out
1143                                         .println("FIRING POST_CHANGE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
1144                         System.out
1145                                         .println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
1146                 }
1147                 if (deltaToNotify != null) {
1148                         // flush now so as to keep listener reactions to post their own
1149                         // deltas for subsequent iteration
1150                         this.flush();
1151
1152                         notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE,
1153                                         listeners, listenerMask, listenerCount);
1154                 }
1155         }
1156
1157         private void fireReconcileDelta(IElementChangedListener[] listeners,
1158                         int[] listenerMask, int listenerCount) {
1159
1160                 IJavaElementDelta deltaToNotify = mergeDeltas(this.reconcileDeltas
1161                                 .values());
1162                 if (DeltaProcessor.VERBOSE) {
1163                         System.out
1164                                         .println("FIRING POST_RECONCILE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
1165                         System.out
1166                                         .println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
1167                 }
1168                 if (deltaToNotify != null) {
1169                         // flush now so as to keep listener reactions to post their own
1170                         // deltas for subsequent iteration
1171                         this.reconcileDeltas = new HashMap();
1172
1173                         notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE,
1174                                         listeners, listenerMask, listenerCount);
1175                 }
1176         }
1177
1178         public void notifyListeners(IJavaElementDelta deltaToNotify, int eventType,
1179                         IElementChangedListener[] listeners, int[] listenerMask,
1180                         int listenerCount) {
1181                 final ElementChangedEvent extraEvent = new ElementChangedEvent(
1182                                 deltaToNotify, eventType);
1183                 for (int i = 0; i < listenerCount; i++) {
1184                         if ((listenerMask[i] & eventType) != 0) {
1185                                 final IElementChangedListener listener = listeners[i];
1186                                 long start = -1;
1187                                 if (DeltaProcessor.VERBOSE) {
1188                                         System.out
1189                                                         .print("Listener #" + (i + 1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$
1190                                         start = System.currentTimeMillis();
1191                                 }
1192                                 // wrap callbacks with Safe runnable for subsequent listeners to
1193                                 // be called when some are causing grief
1194                                 
1195                                 SafeRunner.run(new ISafeRunnable() {
1196                                         public void handleException(Throwable exception) {
1197                                                 Util
1198                                                                 .log(exception,
1199                                                                                 "Exception occurred in listener of Java element change notification"); //$NON-NLS-1$
1200                                         }
1201
1202                                         public void run() throws Exception {
1203                                                 listener.elementChanged(extraEvent);
1204                                         }
1205                                 });
1206                                 if (DeltaProcessor.VERBOSE) {
1207                                         System.out
1208                                                         .println(" -> " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
1209                                 }
1210                         }
1211                 }
1212         }
1213
1214         /**
1215          * Flushes all deltas without firing them.
1216          */
1217         protected void flush() {
1218                 this.javaModelDeltas = new ArrayList();
1219         }
1220
1221         /**
1222          * Flushes ZipFiles cache if there are no more clients.
1223          */
1224         public void flushZipFiles() {
1225                 synchronized (this.zipFiles) {
1226                         Thread currentThread = Thread.currentThread();
1227                         HashMap map = (HashMap) this.zipFiles.remove(currentThread);
1228                         if (map == null)
1229                                 return;
1230                         Iterator iterator = map.values().iterator();
1231                         while (iterator.hasNext()) {
1232                                 try {
1233                                         ZipFile zipFile = (ZipFile) iterator.next();
1234                                         if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
1235                                                 System.out
1236                                                                 .println("(" + currentThread + ") [JavaModelManager.flushZipFiles()] Closing ZipFile on " + zipFile.getName()); //$NON-NLS-1$//$NON-NLS-2$
1237                                         }
1238                                         zipFile.close();
1239                                 } catch (IOException e) {
1240                                 }
1241                         }
1242                 }
1243         }
1244
1245         public DeltaProcessor getDeltaProcessor() {
1246                 return this.deltaState.getDeltaProcessor();
1247         }
1248
1249         /**
1250          * Returns the set of elements which are out of synch with their buffers.
1251          */
1252         protected Map getElementsOutOfSynchWithBuffers() {
1253                 return this.elementsOutOfSynchWithBuffers;
1254         }
1255
1256         // public IndexManager getIndexManager() {
1257         // return this.indexManager;
1258         // }
1259         /**
1260          * Returns the <code>IJavaElement</code> represented by the
1261          * <code>String</code> memento.
1262          */
1263         public IJavaElement getHandleFromMemento(String memento)
1264                         throws JavaModelException {
1265                 if (memento == null) {
1266                         return null;
1267                 }
1268                 JavaModel model = (JavaModel) getJavaModel();
1269                 if (memento.equals("")) { // workspace memento //$NON-NLS-1$
1270                         return model;
1271                 }
1272                 int modelEnd = memento.indexOf(JavaElement.JEM_JAVAPROJECT);
1273                 if (modelEnd == -1) {
1274                         return null;
1275                 }
1276                 boolean returnProject = false;
1277                 int projectEnd = memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENTROOT,
1278                                 modelEnd);
1279                 if (projectEnd == -1) {
1280                         projectEnd = memento.length();
1281                         returnProject = true;
1282                 }
1283                 String projectName = memento.substring(modelEnd + 1, projectEnd);
1284                 JavaProject proj = (JavaProject) model.getJavaProject(projectName);
1285                 if (returnProject) {
1286                         return proj;
1287                 }
1288                 int rootEnd = memento.indexOf(JavaElement.JEM_PACKAGEFRAGMENT,
1289                                 projectEnd + 1);
1290                 // TODO temp-del
1291                 // if (rootEnd == -1) {
1292                 // return model.getHandleFromMementoForRoot(memento, proj, projectEnd,
1293                 // memento.length());
1294                 // }
1295                 // IPackageFragmentRoot root =
1296                 // model.getHandleFromMementoForRoot(memento, proj, projectEnd,
1297                 // rootEnd);
1298                 // if (root == null)
1299                 // return null;
1300                 //
1301                 // int end= memento.indexOf(JavaElement.JEM_COMPILATIONUNIT, rootEnd);
1302                 // if (end == -1) {
1303                 // end= memento.indexOf(JavaElement.JEM_CLASSFILE, rootEnd);
1304                 // if (end == -1) {
1305                 // if (rootEnd + 1 == memento.length()) {
1306                 // return
1307                 // root.getPackageFragment(IPackageFragment.DEFAULT_PACKAGE_NAME);
1308                 // } else {
1309                 // return root.getPackageFragment(memento.substring(rootEnd + 1));
1310                 // }
1311                 // }
1312                 // //deal with class file and binary members
1313                 // return model.getHandleFromMementoForBinaryMembers(memento, root,
1314                 // rootEnd, end);
1315                 // }
1316                 //
1317                 // //deal with compilation units and source members
1318                 // return model.getHandleFromMementoForSourceMembers(memento, root,
1319                 // rootEnd, end);
1320                 return null;
1321         }
1322
1323         // public IndexManager getIndexManager() {
1324         // return this.deltaProcessor.indexManager;
1325         // }
1326
1327         /**
1328          * Returns the info for the element.
1329          */
1330         public Object getInfo(IJavaElement element) {
1331                 return this.cache.getInfo(element);
1332         }
1333
1334         /**
1335          * Returns the handle to the active Java Model.
1336          */
1337         public final JavaModel getJavaModel() {
1338                 return javaModel;
1339         }
1340
1341         /**
1342          * Returns the singleton JavaModelManager
1343          */
1344         public final static JavaModelManager getJavaModelManager() {
1345                 return Manager;
1346         }
1347
1348         /**
1349          * Returns the last built state for the given project, or null if there is
1350          * none. Deserializes the state if necessary.
1351          * 
1352          * For use by image builder and evaluation support only
1353          */
1354         public Object getLastBuiltState(IProject project, IProgressMonitor monitor) {
1355                 if (!JavaProject.hasJavaNature(project))
1356                         return null; // should never be requested on non-Java projects
1357                 PerProjectInfo info = getPerProjectInfo(project, true/*
1358                                                                                                                                  * create if
1359                                                                                                                                  * missing
1360                                                                                                                                  */);
1361                 if (!info.triedRead) {
1362                         info.triedRead = true;
1363                         try {
1364                                 if (monitor != null)
1365                                         monitor.subTask(Util.bind(
1366                                                         "build.readStateProgress", project.getName())); //$NON-NLS-1$
1367                                 info.savedState = readState(project);
1368                         } catch (CoreException e) {
1369                                 e.printStackTrace();
1370                         }
1371                 }
1372                 return info.savedState;
1373         }
1374
1375         /*
1376          * Returns the per-project info for the given project. If specified, create
1377          * the info if the info doesn't exist.
1378          */
1379         public PerProjectInfo getPerProjectInfo(IProject project, boolean create) {
1380                 synchronized (perProjectInfo) { // use the perProjectInfo collection as
1381                                                                                 // its own lock
1382                         PerProjectInfo info = (PerProjectInfo) perProjectInfo.get(project);
1383                         if (info == null && create) {
1384                                 info = new PerProjectInfo(project);
1385                                 perProjectInfo.put(project, info);
1386                         }
1387                         return info;
1388                 }
1389         }
1390
1391         /*
1392          * Returns the per-project info for the given project. If the info doesn't
1393          * exist, check for the project existence and create the info. @throws
1394          * JavaModelException if the project doesn't exist.
1395          */
1396         public PerProjectInfo getPerProjectInfoCheckExistence(IProject project)
1397                         throws JavaModelException {
1398                 JavaModelManager.PerProjectInfo info = getPerProjectInfo(project, false /*
1399                                                                                                                                                                  * don't
1400                                                                                                                                                                  * create
1401                                                                                                                                                                  * info
1402                                                                                                                                                                  */);
1403                 if (info == null) {
1404                         if (!JavaProject.hasJavaNature(project)) {
1405                                 throw ((JavaProject) JavaCore.create(project))
1406                                                 .newNotPresentException();
1407                         }
1408                         info = getPerProjectInfo(project, true /* create info */);
1409                 }
1410                 return info;
1411         }
1412
1413         /*
1414          * Returns the per-working copy info for the given working copy at the given
1415          * path. If it doesn't exist and if create, add a new per-working copy info
1416          * with the given problem requestor. If recordUsage, increment the
1417          * per-working copy info's use count. Returns null if it doesn't exist and
1418          * not create.
1419          */
1420         public PerWorkingCopyInfo getPerWorkingCopyInfo(
1421                         CompilationUnit workingCopy, boolean create, boolean recordUsage,
1422                         IProblemRequestor problemRequestor) {
1423                 synchronized (perWorkingCopyInfos) { // use the perWorkingCopyInfo
1424                                                                                                 // collection as its own lock
1425                         WorkingCopyOwner owner = workingCopy.owner;
1426                         Map workingCopyToInfos = (Map) this.perWorkingCopyInfos.get(owner);
1427                         if (workingCopyToInfos == null && create) {
1428                                 workingCopyToInfos = new HashMap();
1429                                 this.perWorkingCopyInfos.put(owner, workingCopyToInfos);
1430                         }
1431
1432                         PerWorkingCopyInfo info = workingCopyToInfos == null ? null
1433                                         : (PerWorkingCopyInfo) workingCopyToInfos.get(workingCopy);
1434                         if (info == null && create) {
1435                                 info = new PerWorkingCopyInfo(workingCopy, problemRequestor);
1436                                 workingCopyToInfos.put(workingCopy, info);
1437                         }
1438                         if (info != null && recordUsage)
1439                                 info.useCount++;
1440                         return info;
1441                 }
1442         }
1443
1444         /**
1445          * Returns the name of the variables for which an CP variable initializer is
1446          * registered through an extension point
1447          */
1448         public static String[] getRegisteredVariableNames() {
1449
1450                 Plugin jdtCorePlugin = JavaCore.getPlugin();
1451                 if (jdtCorePlugin == null)
1452                         return null;
1453
1454                 ArrayList variableList = new ArrayList(5);
1455                 // IExtensionPoint extension =
1456                 // jdtCorePlugin.getDescriptor().getExtensionPoint(JavaModelManager.CPVARIABLE_INITIALIZER_EXTPOINT_ID);
1457                 // if (extension != null) {
1458                 // IExtension[] extensions = extension.getExtensions();
1459                 // for(int i = 0; i < extensions.length; i++){
1460                 // IConfigurationElement [] configElements =
1461                 // extensions[i].getConfigurationElements();
1462                 // for(int j = 0; j < configElements.length; j++){
1463                 // String varAttribute = configElements[j].getAttribute("variable");
1464                 // //$NON-NLS-1$
1465                 // if (varAttribute != null) variableList.add(varAttribute);
1466                 // }
1467                 // }
1468                 // }
1469                 String[] variableNames = new String[variableList.size()];
1470                 variableList.toArray(variableNames);
1471                 return variableNames;
1472         }
1473
1474         /**
1475          * Returns the name of the container IDs for which an CP container
1476          * initializer is registered through an extension point
1477          */
1478         // public static String[] getRegisteredContainerIDs(){
1479         //
1480         // Plugin jdtCorePlugin = PHPCore.getPlugin();
1481         // if (jdtCorePlugin == null) return null;
1482         //
1483         // ArrayList containerIDList = new ArrayList(5);
1484         // IExtensionPoint extension =
1485         // jdtCorePlugin.getDescriptor().getExtensionPoint(JavaModelManager.CPCONTAINER_INITIALIZER_EXTPOINT_ID);
1486         // if (extension != null) {
1487         // IExtension[] extensions = extension.getExtensions();
1488         // for(int i = 0; i < extensions.length; i++){
1489         // IConfigurationElement [] configElements =
1490         // extensions[i].getConfigurationElements();
1491         // for(int j = 0; j < configElements.length; j++){
1492         // String idAttribute = configElements[j].getAttribute("id"); //$NON-NLS-1$
1493         // if (idAttribute != null) containerIDList.add(idAttribute);
1494         // }
1495         // }
1496         // }
1497         // String[] containerIDs = new String[containerIDList.size()];
1498         // containerIDList.toArray(containerIDs);
1499         // return containerIDs;
1500         // }
1501         /**
1502          * Returns the File to use for saving and restoring the last built state for
1503          * the given project.
1504          */
1505         private File getSerializationFile(IProject project) {
1506                 if (!project.exists())
1507                         return null;
1508                 IPath workingLocation = project.getWorkingLocation(JavaCore.PLUGIN_ID);
1509                 return workingLocation.append("state.dat").toFile(); //$NON-NLS-1$
1510         }
1511
1512         /*
1513          * Returns the temporary cache for newly opened elements for the current
1514          * thread. Creates it if not already created.
1515          */
1516         public HashMap getTemporaryCache() {
1517                 HashMap result = (HashMap) this.temporaryCache.get();
1518                 if (result == null) {
1519                         result = new HashMap();
1520                         this.temporaryCache.set(result);
1521                 }
1522                 return result;
1523         }
1524
1525         /**
1526          * Returns the open ZipFile at the given location. If the ZipFile does not
1527          * yet exist, it is created, opened, and added to the cache of open
1528          * ZipFiles. The location must be a absolute path.
1529          * 
1530          * @exception CoreException
1531          *                If unable to create/open the ZipFile
1532          */
1533         public ZipFile getZipFile(IPath path) throws CoreException {
1534
1535                 synchronized (this.zipFiles) { // TODO: use PeThreadObject which does
1536                                                                                 // synchronization
1537                         Thread currentThread = Thread.currentThread();
1538                         HashMap map = null;
1539                         ZipFile zipFile;
1540                         if ((map = (HashMap) this.zipFiles.get(currentThread)) != null
1541                                         && (zipFile = (ZipFile) map.get(path)) != null) {
1542
1543                                 return zipFile;
1544                         }
1545                         String fileSystemPath = null;
1546                         IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
1547                         IResource file = root.findMember(path);
1548                         if (path.isAbsolute() && file != null) {
1549                                 if (file == null) { // external file
1550                                         fileSystemPath = path.toOSString();
1551                                 } else { // internal resource (not an IFile or not existing)
1552                                         IPath location;
1553                                         if (file.getType() != IResource.FILE
1554                                                         || (location = file.getFullPath()) == null) {
1555                                                 throw new CoreException(
1556                                                                 new Status(
1557                                                                                 IStatus.ERROR,
1558                                                                                 JavaCore.PLUGIN_ID,
1559                                                                                 -1,
1560                                                                                 Util
1561                                                                                                 .bind(
1562                                                                                                                 "file.notFound", path.toString()), null)); //$NON-NLS-1$
1563                                         }
1564                                         fileSystemPath = location.toOSString();
1565                                 }
1566                         } else if (!path.isAbsolute()) {
1567                                 file = root.getFile(path);
1568                                 if (file == null || file.getType() != IResource.FILE) {
1569                                         throw new CoreException(new Status(IStatus.ERROR,
1570                                                         JavaCore.PLUGIN_ID, -1, Util.bind(
1571                                                                         "file.notFound", path.toString()), null)); //$NON-NLS-1$
1572                                 }
1573                                 IPath location = file.getFullPath();
1574                                 if (location == null) {
1575                                         throw new CoreException(new Status(IStatus.ERROR,
1576                                                         JavaCore.PLUGIN_ID, -1, Util.bind(
1577                                                                         "file.notFound", path.toString()), null)); //$NON-NLS-1$
1578                                 }
1579                                 fileSystemPath = location.toOSString();
1580                         } else {
1581                                 fileSystemPath = path.toOSString();
1582                         }
1583
1584                         try {
1585                                 if (ZIP_ACCESS_VERBOSE) {
1586                                         System.out
1587                                                         .println("(" + currentThread + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + fileSystemPath); //$NON-NLS-1$ //$NON-NLS-2$
1588                                 }
1589                                 zipFile = new ZipFile(fileSystemPath);
1590                                 if (map != null) {
1591                                         map.put(path, zipFile);
1592                                 }
1593                                 return zipFile;
1594                         } catch (IOException e) {
1595                                 throw new CoreException(new Status(Status.ERROR,
1596                                                 JavaCore.PLUGIN_ID, -1,
1597                                                 Util.bind("status.IOException"), e)); //$NON-NLS-1$
1598                         }
1599                 }
1600         }
1601
1602         /*
1603          * Returns whether there is a temporary cache for the current thread.
1604          */
1605         public boolean hasTemporaryCache() {
1606                 return this.temporaryCache.get() != null;
1607         }
1608
1609         // public void loadVariablesAndContainers() throws CoreException {
1610         //
1611         // // backward compatibility, consider persistent property
1612         // QualifiedName qName = new QualifiedName(PHPCore.PLUGIN_ID, "variables");
1613         // //$NON-NLS-1$
1614         // String xmlString =
1615         // ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName);
1616         //
1617         // try {
1618         // if (xmlString != null){
1619         // StringReader reader = new StringReader(xmlString);
1620         // Element cpElement;
1621         // try {
1622         // DocumentBuilder parser =
1623         // DocumentBuilderFactory.newInstance().newDocumentBuilder();
1624         // cpElement = parser.parse(new InputSource(reader)).getDocumentElement();
1625         // } catch(SAXException e) {
1626         // return;
1627         // } catch(ParserConfigurationException e){
1628         // return;
1629         // } finally {
1630         // reader.close();
1631         // }
1632         // if (cpElement == null) return;
1633         // if (!cpElement.getNodeName().equalsIgnoreCase("variables")) {
1634         // //$NON-NLS-1$
1635         // return;
1636         // }
1637         //
1638         // NodeList list= cpElement.getChildNodes();
1639         // int length= list.getLength();
1640         // for (int i= 0; i < length; ++i) {
1641         // Node node= list.item(i);
1642         // short type= node.getNodeType();
1643         // if (type == Node.ELEMENT_NODE) {
1644         // Element element= (Element) node;
1645         // if (element.getNodeName().equalsIgnoreCase("variable")) { //$NON-NLS-1$
1646         // variablePut(
1647         // element.getAttribute("name"), //$NON-NLS-1$
1648         // new Path(element.getAttribute("path"))); //$NON-NLS-1$
1649         // }
1650         // }
1651         // }
1652         // }
1653         // } catch(IOException e){
1654         // } finally {
1655         // if (xmlString != null){
1656         // ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName,
1657         // null); // flush old one
1658         // }
1659         //
1660         // }
1661         //
1662         // // load variables and containers from preferences into cache
1663         // Preferences preferences =
1664         // PHPeclipsePlugin.getDefault().getPluginPreferences();
1665
1666         // // only get variable from preferences not set to their default
1667         // String[] propertyNames = preferences.propertyNames();
1668         // int variablePrefixLength = CP_VARIABLE_PREFERENCES_PREFIX.length();
1669         // for (int i = 0; i < propertyNames.length; i++){
1670         // String propertyName = propertyNames[i];
1671         // if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)){
1672         // String varName = propertyName.substring(variablePrefixLength);
1673         // IPath varPath = new Path(preferences.getString(propertyName).trim());
1674         //
1675         // Variables.put(varName, varPath);
1676         // PreviousSessionVariables.put(varName, varPath);
1677         // }
1678         // if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)){
1679         // recreatePersistedContainer(propertyName,
1680         // preferences.getString(propertyName), true/*add to container values*/);
1681         // }
1682         // }
1683         // // override persisted values for variables which have a registered
1684         // initializer
1685         // String[] registeredVariables = getRegisteredVariableNames();
1686         // for (int i = 0; i < registeredVariables.length; i++) {
1687         // String varName = registeredVariables[i];
1688         // Variables.put(varName, null); // reset variable, but leave its entry in
1689         // the Map, so it will be part of variable names.
1690         // }
1691         // // override persisted values for containers which have a registered
1692         // initializer
1693         // String[] registeredContainerIDs = getRegisteredContainerIDs();
1694         // for (int i = 0; i < registeredContainerIDs.length; i++) {
1695         // String containerID = registeredContainerIDs[i];
1696         // Iterator projectIterator = Containers.keySet().iterator();
1697         // while (projectIterator.hasNext()){
1698         // IJavaProject project = (IJavaProject)projectIterator.next();
1699         // Map projectContainers = (Map)Containers.get(project);
1700         // if (projectContainers != null){
1701         // Iterator containerIterator = projectContainers.keySet().iterator();
1702         // while (containerIterator.hasNext()){
1703         // IPath containerPath = (IPath)containerIterator.next();
1704         // if (containerPath.segment(0).equals(containerID)) { // registered
1705         // container
1706         // projectContainers.put(containerPath, null); // reset container value, but
1707         // leave entry in Map
1708         // }
1709         // }
1710         // }
1711         // }
1712         // }
1713         // }
1714
1715         /**
1716          * Merged all awaiting deltas.
1717          */
1718         public IJavaElementDelta mergeDeltas(Collection deltas) {
1719                 if (deltas.size() == 0)
1720                         return null;
1721                 if (deltas.size() == 1)
1722                         return (IJavaElementDelta) deltas.iterator().next();
1723
1724                 if (DeltaProcessor.VERBOSE) {
1725                         System.out
1726                                         .println("MERGING " + deltas.size() + " DELTAS [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1727                 }
1728
1729                 Iterator iterator = deltas.iterator();
1730                 IJavaElement javaModel = this.getJavaModel();
1731                 JavaElementDelta rootDelta = new JavaElementDelta(javaModel);
1732                 boolean insertedTree = false;
1733                 while (iterator.hasNext()) {
1734                         JavaElementDelta delta = (JavaElementDelta) iterator.next();
1735                         if (DeltaProcessor.VERBOSE) {
1736                                 System.out.println(delta.toString());
1737                         }
1738                         IJavaElement element = delta.getElement();
1739                         if (javaModel.equals(element)) {
1740                                 IJavaElementDelta[] children = delta.getAffectedChildren();
1741                                 for (int j = 0; j < children.length; j++) {
1742                                         JavaElementDelta projectDelta = (JavaElementDelta) children[j];
1743                                         rootDelta.insertDeltaTree(projectDelta.getElement(),
1744                                                         projectDelta);
1745                                         insertedTree = true;
1746                                 }
1747                                 IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
1748                                 if (resourceDeltas != null) {
1749                                         for (int i = 0, length = resourceDeltas.length; i < length; i++) {
1750                                                 rootDelta.addResourceDelta(resourceDeltas[i]);
1751                                                 insertedTree = true;
1752                                         }
1753                                 }
1754                         } else {
1755                                 rootDelta.insertDeltaTree(element, delta);
1756                                 insertedTree = true;
1757                         }
1758                 }
1759                 if (insertedTree) {
1760                         return rootDelta;
1761                 } else {
1762                         return null;
1763                 }
1764         }
1765
1766         /**
1767          * Returns the info for this element without disturbing the cache ordering.
1768          */
1769         // TODO: should be synchronized, could answer unitialized info or if cache
1770         // is in middle of rehash, could even answer distinct element info
1771         protected Object peekAtInfo(IJavaElement element) {
1772                 return this.cache.peekAtInfo(element);
1773         }
1774
1775         /**
1776          * @see ISaveParticipant
1777          */
1778         public void prepareToSave(ISaveContext context) throws CoreException {
1779         }
1780
1781         protected void putInfo(IJavaElement element, Object info) {
1782                 this.cache.putInfo(element, info);
1783         }
1784
1785         /*
1786          * Puts the infos in the given map (keys are IJavaElements and values are
1787          * JavaElementInfos) in the Java model cache in an atomic way. First checks
1788          * that the info for the opened element (or one of its ancestors) has not
1789          * been added to the cache. If it is the case, another thread has opened the
1790          * element (or one of its ancestors). So returns without updating the cache.
1791          */
1792         protected synchronized void putInfos(IJavaElement openedElement,
1793                         Map newElements) {
1794                 // remove children
1795                 Object existingInfo = this.cache.peekAtInfo(openedElement);
1796                 if (openedElement instanceof IParent
1797                                 && existingInfo instanceof JavaElementInfo) {
1798                         IJavaElement[] children = ((JavaElementInfo) existingInfo)
1799                                         .getChildren();
1800                         for (int i = 0, size = children.length; i < size; ++i) {
1801                                 JavaElement child = (JavaElement) children[i];
1802                                 try {
1803                                         child.close();
1804                                 } catch (JavaModelException e) {
1805                                         // ignore
1806                                 }
1807                         }
1808                 }
1809
1810                 Iterator iterator = newElements.keySet().iterator();
1811                 while (iterator.hasNext()) {
1812                         IJavaElement element = (IJavaElement) iterator.next();
1813                         Object info = newElements.get(element);
1814                         this.cache.putInfo(element, info);
1815                 }
1816         }
1817
1818         /**
1819          * Reads the build state for the relevant project.
1820          */
1821         protected Object readState(IProject project) throws CoreException {
1822                 File file = getSerializationFile(project);
1823                 if (file != null && file.exists()) {
1824                         try {
1825                                 DataInputStream in = new DataInputStream(
1826                                                 new BufferedInputStream(new FileInputStream(file)));
1827                                 try {
1828                                         String pluginID = in.readUTF();
1829                                         if (!pluginID.equals(JavaCore.PLUGIN_ID))
1830                                                 throw new IOException(Util
1831                                                                 .bind("build.wrongFileFormat")); //$NON-NLS-1$
1832                                         String kind = in.readUTF();
1833                                         if (!kind.equals("STATE")) //$NON-NLS-1$
1834                                                 throw new IOException(Util
1835                                                                 .bind("build.wrongFileFormat")); //$NON-NLS-1$
1836                                         if (in.readBoolean())
1837                                                 return PHPBuilder.readState(project, in);
1838                                         if (PHPBuilder.DEBUG)
1839                                                 System.out
1840                                                                 .println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$
1841                                 } finally {
1842                                         in.close();
1843                                 }
1844                         } catch (Exception e) {
1845                                 e.printStackTrace();
1846                                 throw new CoreException(
1847                                                 new Status(
1848                                                                 IStatus.ERROR,
1849                                                                 JavaCore.PLUGIN_ID,
1850                                                                 Platform.PLUGIN_ERROR,
1851                                                                 "Error reading last build state for project " + project.getName(), e)); //$NON-NLS-1$
1852                         }
1853                 }
1854                 return null;
1855         }
1856
1857         // public static void recreatePersistedContainer(String propertyName, String
1858         // containerString, boolean addToContainerValues) {
1859         // int containerPrefixLength = CP_CONTAINER_PREFERENCES_PREFIX.length();
1860         // int index = propertyName.indexOf('|', containerPrefixLength);
1861         // if (containerString != null) containerString = containerString.trim();
1862         // if (index > 0) {
1863         // final String projectName = propertyName.substring(containerPrefixLength,
1864         // index).trim();
1865         // JavaProject project =
1866         // (JavaProject)getJavaModelManager().getJavaModel().getJavaProject(projectName);
1867         // final IPath containerPath = new
1868         // Path(propertyName.substring(index+1).trim());
1869         //
1870         // if (containerString == null || containerString.equals(CP_ENTRY_IGNORE)) {
1871         // containerPut(project, containerPath, null);
1872         // } else {
1873         // final IClasspathEntry[] containerEntries =
1874         // project.decodeClasspath(containerString, false, false);
1875         // if (containerEntries != null && containerEntries !=
1876         // JavaProject.INVALID_CLASSPATH) {
1877         // IClasspathContainer container = new IClasspathContainer() {
1878         // public IClasspathEntry[] getClasspathEntries() {
1879         // return containerEntries;
1880         // }
1881         // public String getDescription() {
1882         // return "Persisted container ["+containerPath+" for project ["+
1883         // projectName+"]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
1884         // }
1885         // public int getKind() {
1886         // return 0;
1887         // }
1888         // public IPath getPath() {
1889         // return containerPath;
1890         // }
1891         // public String toString() {
1892         // return getDescription();
1893         // }
1894         //
1895         // };
1896         // if (addToContainerValues) {
1897         // containerPut(project, containerPath, container);
1898         // }
1899         // Map projectContainers = (Map)PreviousSessionContainers.get(project);
1900         // if (projectContainers == null){
1901         // projectContainers = new HashMap(1);
1902         // PreviousSessionContainers.put(project, projectContainers);
1903         // }
1904         // projectContainers.put(containerPath, container);
1905         // }
1906         // }
1907         // }
1908         // }
1909
1910         /**
1911          * Registers the given delta with this manager.
1912          */
1913         protected void registerJavaModelDelta(IJavaElementDelta delta) {
1914                 this.javaModelDeltas.add(delta);
1915         }
1916
1917         /**
1918          * Remembers the given scope in a weak set (so no need to remove it: it will
1919          * be removed by the garbage collector)
1920          */
1921         // public void rememberScope(AbstractSearchScope scope) {
1922         // // NB: The value has to be null so as to not create a strong reference on
1923         // the scope
1924         // this.scopes.put(scope, null);
1925         // }
1926         /**
1927          * removeElementChangedListener method comment.
1928          */
1929         public void removeElementChangedListener(IElementChangedListener listener) {
1930
1931                 for (int i = 0; i < this.elementChangedListenerCount; i++) {
1932
1933                         if (this.elementChangedListeners[i].equals(listener)) {
1934
1935                                 // need to clone defensively since we might be in the middle of
1936                                 // listener notifications (#fire)
1937                                 int length = this.elementChangedListeners.length;
1938                                 IElementChangedListener[] newListeners = new IElementChangedListener[length];
1939                                 System.arraycopy(this.elementChangedListeners, 0, newListeners,
1940                                                 0, i);
1941                                 int[] newMasks = new int[length];
1942                                 System.arraycopy(this.elementChangedListenerMasks, 0, newMasks,
1943                                                 0, i);
1944
1945                                 // copy trailing listeners
1946                                 int trailingLength = this.elementChangedListenerCount - i - 1;
1947                                 if (trailingLength > 0) {
1948                                         System.arraycopy(this.elementChangedListeners, i + 1,
1949                                                         newListeners, i, trailingLength);
1950                                         System.arraycopy(this.elementChangedListenerMasks, i + 1,
1951                                                         newMasks, i, trailingLength);
1952                                 }
1953
1954                                 // update manager listener state (#fire need to iterate over
1955                                 // original listeners through a local variable to hold onto
1956                                 // the original ones)
1957                                 this.elementChangedListeners = newListeners;
1958                                 this.elementChangedListenerMasks = newMasks;
1959                                 this.elementChangedListenerCount--;
1960                                 return;
1961                         }
1962                 }
1963         }
1964
1965         /**
1966          * Remembers the given scope in a weak set (so no need to remove it: it will
1967          * be removed by the garbage collector)
1968          */
1969         // public void rememberScope(AbstractSearchScope scope) {
1970         // // NB: The value has to be null so as to not create a strong reference on
1971         // the scope
1972         // this.searchScopes.put(scope, null);
1973         // }
1974         /*
1975          * Removes all cached info for the given element (including all children)
1976          * from the cache. Returns the info for the given element, or null if it was
1977          * closed.
1978          */
1979         public synchronized Object removeInfoAndChildren(JavaElement element)
1980                         throws JavaModelException {
1981                 Object info = this.cache.peekAtInfo(element);
1982                 if (info != null) {
1983                         boolean wasVerbose = false;
1984                         try {
1985                                 if (VERBOSE) {
1986                                         System.out
1987                                                         .println("CLOSING Element (" + Thread.currentThread() + "): " + element.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
1988                                         wasVerbose = true;
1989                                         VERBOSE = false;
1990                                 }
1991                                 element.closing(info);
1992                                 if (element instanceof IParent
1993                                                 && info instanceof JavaElementInfo) {
1994                                         IJavaElement[] children = ((JavaElementInfo) info)
1995                                                         .getChildren();
1996                                         for (int i = 0, size = children.length; i < size; ++i) {
1997                                                 JavaElement child = (JavaElement) children[i];
1998                                                 child.close();
1999                                         }
2000                                 }
2001                                 this.cache.removeInfo(element);
2002                                 if (wasVerbose) {
2003                                         System.out
2004                                                         .println("-> Package cache size = " + this.cache.pkgSize()); //$NON-NLS-1$
2005                                         System.out
2006                                                         .println("-> Openable cache filling ratio = " + NumberFormat.getInstance().format(this.cache.openableFillingRatio()) + "%"); //$NON-NLS-1$//$NON-NLS-2$
2007                                 }
2008                         } finally {
2009                                 JavaModelManager.VERBOSE = wasVerbose;
2010                         }
2011                         return info;
2012                 }
2013                 return null;
2014         }
2015
2016         public void removePerProjectInfo(JavaProject javaProject) {
2017                 synchronized (perProjectInfo) { // use the perProjectInfo collection as
2018                                                                                 // its own lock
2019                         IProject project = javaProject.getProject();
2020                         PerProjectInfo info = (PerProjectInfo) perProjectInfo.get(project);
2021                         if (info != null) {
2022                                 perProjectInfo.remove(project);
2023                         }
2024                 }
2025         }
2026
2027         /*
2028          * Resets the temporary cache for newly created elements to null.
2029          */
2030         public void resetTemporaryCache() {
2031                 this.temporaryCache.set(null);
2032         }
2033
2034         /**
2035          * @see ISaveParticipant
2036          */
2037         public void rollback(ISaveContext context) {
2038         }
2039
2040         private void saveState(PerProjectInfo info, ISaveContext context)
2041                         throws CoreException {
2042
2043                 // passed this point, save actions are non trivial
2044                 if (context.getKind() == ISaveContext.SNAPSHOT)
2045                         return;
2046
2047                 // save built state
2048                 if (info.triedRead)
2049                         saveBuiltState(info);
2050         }
2051
2052         /**
2053          * Saves the built state for the project.
2054          */
2055         private void saveBuiltState(PerProjectInfo info) throws CoreException {
2056                 if (PHPBuilder.DEBUG)
2057                         System.out.println(Util.bind(
2058                                         "build.saveStateProgress", info.project.getName())); //$NON-NLS-1$
2059                 File file = getSerializationFile(info.project);
2060                 if (file == null)
2061                         return;
2062                 long t = System.currentTimeMillis();
2063                 try {
2064                         DataOutputStream out = new DataOutputStream(
2065                                         new BufferedOutputStream(new FileOutputStream(file)));
2066                         try {
2067                                 out.writeUTF(JavaCore.PLUGIN_ID);
2068                                 out.writeUTF("STATE"); //$NON-NLS-1$
2069                                 if (info.savedState == null) {
2070                                         out.writeBoolean(false);
2071                                 } else {
2072                                         out.writeBoolean(true);
2073                                         PHPBuilder.writeState(info.savedState, out);
2074                                 }
2075                         } finally {
2076                                 out.close();
2077                         }
2078                 } catch (RuntimeException e) {
2079                         try {
2080                                 file.delete();
2081                         } catch (SecurityException se) {
2082                         }
2083                         throw new CoreException(
2084                                         new Status(
2085                                                         IStatus.ERROR,
2086                                                         JavaCore.PLUGIN_ID,
2087                                                         Platform.PLUGIN_ERROR,
2088                                                         Util
2089                                                                         .bind(
2090                                                                                         "build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
2091                 } catch (IOException e) {
2092                         try {
2093                                 file.delete();
2094                         } catch (SecurityException se) {
2095                         }
2096                         throw new CoreException(
2097                                         new Status(
2098                                                         IStatus.ERROR,
2099                                                         JavaCore.PLUGIN_ID,
2100                                                         Platform.PLUGIN_ERROR,
2101                                                         Util
2102                                                                         .bind(
2103                                                                                         "build.cannotSaveState", info.project.getName()), e)); //$NON-NLS-1$
2104                 }
2105                 if (PHPBuilder.DEBUG) {
2106                         t = System.currentTimeMillis() - t;
2107                         System.out.println(Util.bind(
2108                                         "build.saveStateComplete", String.valueOf(t))); //$NON-NLS-1$
2109                 }
2110         }
2111
2112         private synchronized Map containerClone(IJavaProject project) {
2113                 Map originalProjectContainers = (Map) JavaModelManager.containers.get(project);
2114                 if (originalProjectContainers == null)
2115                         return null;
2116                 Map projectContainers = new HashMap(originalProjectContainers.size());
2117                 projectContainers.putAll(originalProjectContainers);
2118                 return projectContainers;
2119         }
2120
2121         /**
2122          * @see ISaveParticipant
2123          */
2124         public void saving(ISaveContext context) throws CoreException {
2125
2126                 // save container values on snapshot/full save
2127                 Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
2128                 IJavaProject[] projects = getJavaModel().getJavaProjects();
2129                 for (int i = 0, length = projects.length; i < length; i++) {
2130                         IJavaProject project = projects[i];
2131                         // clone while iterating (see
2132                         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=59638)
2133                         Map projectContainers = containerClone(project);
2134                         if (projectContainers == null)
2135                                 continue;
2136                         for (Iterator keys = projectContainers.keySet().iterator(); keys
2137                                         .hasNext();) {
2138                                 IPath containerPath = (IPath) keys.next();
2139                                 // IClasspathContainer container = (IClasspathContainer)
2140                                 // projectContainers.get(containerPath);
2141                                 String containerKey = CP_CONTAINER_PREFERENCES_PREFIX
2142                                                 + project.getElementName() + "|" + containerPath;//$NON-NLS-1$
2143                                 String containerString = CP_ENTRY_IGNORE;
2144                                 // try {
2145                                 // if (container != null) {
2146                                 // containerString =
2147                                 // ((JavaProject)project).encodeClasspath(container.getClasspathEntries(),
2148                                 // null, false);
2149                                 // }
2150                                 // } catch(JavaModelException e){
2151                                 // // could not encode entry: leave it as CP_ENTRY_IGNORE
2152                                 // }
2153                                 preferences.setDefault(containerKey, CP_ENTRY_IGNORE); // use
2154                                                                                                                                                 // this
2155                                                                                                                                                 // default
2156                                                                                                                                                 // to
2157                                                                                                                                                 // get
2158                                                                                                                                                 // rid
2159                                                                                                                                                 // of
2160                                                                                                                                                 // removed
2161                                                                                                                                                 // ones
2162                                 preferences.setValue(containerKey, containerString);
2163                         }
2164                 }
2165                 JavaCore.getPlugin().savePluginPreferences();
2166
2167                 // if (context.getKind() == ISaveContext.FULL_SAVE) {
2168                 // // will need delta since this save (see
2169                 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658)
2170                 // context.needDelta();
2171                 //
2172                 // // clean up indexes on workspace full save
2173                 // // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52347)
2174                 // IndexManager manager = this.indexManager;
2175                 // if (manager != null) {
2176                 // manager.cleanUpIndexes();
2177                 // }
2178                 // }
2179
2180                 IProject savedProject = context.getProject();
2181                 if (savedProject != null) {
2182                         if (!JavaProject.hasJavaNature(savedProject))
2183                                 return; // ignore
2184                         PerProjectInfo info = getPerProjectInfo(savedProject, true /*
2185                                                                                                                                                  * create
2186                                                                                                                                                  * info
2187                                                                                                                                                  */);
2188                         saveState(info, context);
2189                         return;
2190                 }
2191
2192                 ArrayList vStats = null; // lazy initialized
2193                 for (Iterator iter = perProjectInfo.values().iterator(); iter.hasNext();) {
2194                         try {
2195                                 PerProjectInfo info = (PerProjectInfo) iter.next();
2196                                 saveState(info, context);
2197                         } catch (CoreException e) {
2198                                 if (vStats == null)
2199                                         vStats = new ArrayList();
2200                                 vStats.add(e.getStatus());
2201                         }
2202                 }
2203                 if (vStats != null) {
2204                         IStatus[] stats = new IStatus[vStats.size()];
2205                         vStats.toArray(stats);
2206                         throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID,
2207                                         IStatus.ERROR, stats,
2208                                         Util.bind("build.cannotSaveStates"), null)); //$NON-NLS-1$
2209                 }
2210         }
2211
2212         /**
2213          * @see ISaveParticipant
2214          */
2215         // public void saving(ISaveContext context) throws CoreException {
2216         //
2217         // IProject savedProject = context.getProject();
2218         // if (savedProject != null) {
2219         // if (!JavaProject.hasJavaNature(savedProject)) return; // ignore
2220         // PerProjectInfo info = getPerProjectInfo(savedProject, true /* create info
2221         // */);
2222         // saveState(info, context);
2223         // return;
2224         // }
2225         //
2226         // ArrayList vStats= null; // lazy initialized
2227         // for (Iterator iter = perProjectInfo.values().iterator(); iter.hasNext();)
2228         // {
2229         // try {
2230         // PerProjectInfo info = (PerProjectInfo) iter.next();
2231         // saveState(info, context);
2232         // } catch (CoreException e) {
2233         // if (vStats == null)
2234         // vStats= new ArrayList();
2235         // vStats.add(e.getStatus());
2236         // }
2237         // }
2238         // if (vStats != null) {
2239         // IStatus[] stats= new IStatus[vStats.size()];
2240         // vStats.toArray(stats);
2241         // throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID,
2242         // IStatus.ERROR, stats, Util.bind("build.cannotSaveStates"), null));
2243         // //$NON-NLS-1$
2244         // }
2245         // }
2246         /**
2247          * Record the order in which to build the java projects (batch build). This
2248          * order is based on the projects classpath settings.
2249          */
2250         protected void setBuildOrder(String[] javaBuildOrder)
2251                         throws JavaModelException {
2252
2253                 // optional behaviour
2254                 // possible value of index 0 is Compute
2255                 if (!JavaCore.COMPUTE.equals(JavaCore
2256                                 .getOption(JavaCore.CORE_JAVA_BUILD_ORDER)))
2257                         return; // cannot be customized at project level
2258
2259                 if (javaBuildOrder == null || javaBuildOrder.length <= 1)
2260                         return;
2261
2262                 IWorkspace workspace = ResourcesPlugin.getWorkspace();
2263                 IWorkspaceDescription description = workspace.getDescription();
2264                 String[] wksBuildOrder = description.getBuildOrder();
2265
2266                 String[] newOrder;
2267                 if (wksBuildOrder == null) {
2268                         newOrder = javaBuildOrder;
2269                 } else {
2270                         // remove projects which are already mentionned in java builder
2271                         // order
2272                         int javaCount = javaBuildOrder.length;
2273                         HashMap newSet = new HashMap(javaCount); // create a set for fast
2274                                                                                                                 // check
2275                         for (int i = 0; i < javaCount; i++) {
2276                                 newSet.put(javaBuildOrder[i], javaBuildOrder[i]);
2277                         }
2278                         int removed = 0;
2279                         int oldCount = wksBuildOrder.length;
2280                         for (int i = 0; i < oldCount; i++) {
2281                                 if (newSet.containsKey(wksBuildOrder[i])) {
2282                                         wksBuildOrder[i] = null;
2283                                         removed++;
2284                                 }
2285                         }
2286                         // add Java ones first
2287                         newOrder = new String[oldCount - removed + javaCount];
2288                         System.arraycopy(javaBuildOrder, 0, newOrder, 0, javaCount); // java
2289                                                                                                                                                         // projects
2290                                                                                                                                                         // are
2291                                                                                                                                                         // built
2292                                                                                                                                                         // first
2293
2294                         // copy previous items in their respective order
2295                         int index = javaCount;
2296                         for (int i = 0; i < oldCount; i++) {
2297                                 if (wksBuildOrder[i] != null) {
2298                                         newOrder[index++] = wksBuildOrder[i];
2299                                 }
2300                         }
2301                 }
2302                 // commit the new build order out
2303                 description.setBuildOrder(newOrder);
2304                 try {
2305                         workspace.setDescription(description);
2306                 } catch (CoreException e) {
2307                         throw new JavaModelException(e);
2308                 }
2309         }
2310
2311         /**
2312          * Sets the last built state for the given project, or null to reset it.
2313          */
2314         public void setLastBuiltState(IProject project, Object state) {
2315                 if (!JavaProject.hasJavaNature(project))
2316                         return; // should never be requested on non-Java projects
2317                 PerProjectInfo info = getPerProjectInfo(project, true /*
2318                                                                                                                                  * create if
2319                                                                                                                                  * missing
2320                                                                                                                                  */);
2321                 info.triedRead = true; // no point trying to re-read once using setter
2322                 info.savedState = state;
2323                 if (state == null) { // delete state file to ensure a full build
2324                                                                 // happens if the workspace crashes
2325                         try {
2326                                 File file = getSerializationFile(project);
2327                                 if (file != null && file.exists())
2328                                         file.delete();
2329                         } catch (SecurityException se) {
2330                         }
2331                 }
2332         }
2333
2334         public void shutdown() {
2335                 // TODO temp-del
2336                 // if (this.deltaProcessor.indexManager != null){ // no more indexing
2337                 // this.deltaProcessor.indexManager.shutdown();
2338                 // }
2339                 try {
2340                         IJavaModel model = this.getJavaModel();
2341                         if (model != null) {
2342
2343                                 model.close();
2344                         }
2345                 } catch (JavaModelException e) {
2346                 }
2347         }
2348
2349         /**
2350          * Turns the firing mode to on. That is, deltas that are/have been
2351          * registered will be fired.
2352          */
2353         public void startDeltas() {
2354                 this.isFiring = true;
2355         }
2356
2357         /**
2358          * Turns the firing mode to off. That is, deltas that are/have been
2359          * registered will not be fired until deltas are started again.
2360          */
2361         public void stopDeltas() {
2362                 this.isFiring = false;
2363         }
2364
2365         /**
2366          * Update Java Model given some delta
2367          */
2368         public void updateJavaModel(IJavaElementDelta customDelta) {
2369
2370                 if (customDelta == null) {
2371                         for (int i = 0, length = this.javaModelDeltas.size(); i < length; i++) {
2372                                 IJavaElementDelta delta = (IJavaElementDelta) this.javaModelDeltas
2373                                                 .get(i);
2374                                 this.modelUpdater.processJavaDelta(delta);
2375                         }
2376                 } else {
2377                         this.modelUpdater.processJavaDelta(customDelta);
2378                 }
2379         }
2380
2381         public static IPath variableGet(String variableName) {
2382                 return (IPath) Variables.get(variableName);
2383         }
2384
2385         public static String[] variableNames() {
2386                 int length = Variables.size();
2387                 String[] result = new String[length];
2388                 Iterator vars = Variables.keySet().iterator();
2389                 int index = 0;
2390                 while (vars.hasNext()) {
2391                         result[index++] = (String) vars.next();
2392                 }
2393                 return result;
2394         }
2395
2396         public static void variablePut(String variableName, IPath variablePath) {
2397
2398                 // update cache - do not only rely on listener refresh
2399                 if (variablePath == null) {
2400                         Variables.remove(variableName);
2401                         PreviousSessionVariables.remove(variableName);
2402                 } else {
2403                         Variables.put(variableName, variablePath);
2404                 }
2405
2406                 // do not write out intermediate initialization value
2407                 if (variablePath == JavaModelManager.VariableInitializationInProgress) {
2408                         return;
2409                 }
2410                 Preferences preferences = JavaCore.getPlugin().getPluginPreferences();
2411                 String variableKey = CP_VARIABLE_PREFERENCES_PREFIX + variableName;
2412                 String variableString = variablePath == null ? CP_ENTRY_IGNORE
2413                                 : variablePath.toString();
2414                 preferences.setDefault(variableKey, CP_ENTRY_IGNORE); // use this
2415                                                                                                                                 // default to
2416                                                                                                                                 // get rid of
2417                                                                                                                                 // removed ones
2418                 preferences.setValue(variableKey, variableString);
2419                 JavaCore.getPlugin().savePluginPreferences();
2420         }
2421
2422         /*
2423          * Returns all the working copies which have the given owner. Adds the
2424          * working copies of the primary owner if specified. Returns null if it has
2425          * none.
2426          */
2427         public ICompilationUnit[] getWorkingCopies(WorkingCopyOwner owner,
2428                         boolean addPrimary) {
2429                 synchronized (perWorkingCopyInfos) {
2430                         ICompilationUnit[] primaryWCs = addPrimary
2431                                         && owner != DefaultWorkingCopyOwner.PRIMARY ? getWorkingCopies(
2432                                         DefaultWorkingCopyOwner.PRIMARY, false)
2433                                         : null;
2434                         Map workingCopyToInfos = (Map) perWorkingCopyInfos.get(owner);
2435                         if (workingCopyToInfos == null)
2436                                 return primaryWCs;
2437                         int primaryLength = primaryWCs == null ? 0 : primaryWCs.length;
2438                         int size = workingCopyToInfos.size(); // note size is > 0
2439                                                                                                         // otherwise
2440                                                                                                         // pathToPerWorkingCopyInfos
2441                                                                                                         // would be null
2442                         ICompilationUnit[] result = new ICompilationUnit[primaryLength
2443                                         + size];
2444                         if (primaryWCs != null) {
2445                                 System.arraycopy(primaryWCs, 0, result, 0, primaryLength);
2446                         }
2447                         Iterator iterator = workingCopyToInfos.values().iterator();
2448                         int index = primaryLength;
2449                         while (iterator.hasNext()) {
2450                                 result[index++] = ((JavaModelManager.PerWorkingCopyInfo) iterator
2451                                                 .next()).getWorkingCopy();
2452                         }
2453                         return result;
2454                 }
2455         }
2456
2457         /*
2458          * A HashSet that contains the IJavaProject whose classpath is being
2459          * resolved.
2460          */
2461         private ThreadLocal classpathsBeingResolved = new ThreadLocal();
2462
2463         private HashSet getClasspathBeingResolved() {
2464                 HashSet result = (HashSet) this.classpathsBeingResolved.get();
2465                 if (result == null) {
2466                         result = new HashSet();
2467                         this.classpathsBeingResolved.set(result);
2468                 }
2469                 return result;
2470         }
2471
2472         public boolean isClasspathBeingResolved(IJavaProject project) {
2473                 return getClasspathBeingResolved().contains(project);
2474         }
2475
2476         public void setClasspathBeingResolved(IJavaProject project,
2477                         boolean classpathIsResolved) {
2478                 if (classpathIsResolved) {
2479                         getClasspathBeingResolved().add(project);
2480                 } else {
2481                         getClasspathBeingResolved().remove(project);
2482                 }
2483         }
2484 }