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