2a0a6808c0c94cd2dea58e96db556465f918a696
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / JavaProject.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.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.StringReader;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Hashtable;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32
33 import javax.xml.parsers.DocumentBuilder;
34 import javax.xml.parsers.DocumentBuilderFactory;
35 import javax.xml.parsers.ParserConfigurationException;
36 import javax.xml.parsers.SAXParserFactory;
37
38 import net.sourceforge.phpdt.core.IClasspathEntry;
39 import net.sourceforge.phpdt.core.IJavaElement;
40 import net.sourceforge.phpdt.core.IJavaModelMarker;
41 import net.sourceforge.phpdt.core.IJavaModelStatus;
42 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
43 import net.sourceforge.phpdt.core.IJavaProject;
44 import net.sourceforge.phpdt.core.IPackageFragment;
45 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
46 import net.sourceforge.phpdt.core.JavaModelException;
47 import net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment;
48 import net.sourceforge.phpdt.internal.compiler.util.ObjectVector;
49 import net.sourceforge.phpdt.internal.corext.Assert;
50 import net.sourceforge.phpeclipse.LoadPathEntry;
51 import net.sourceforge.phpeclipse.PHPCore;
52 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
53
54 import org.apache.xerces.dom.DocumentImpl;
55 import org.apache.xml.serialize.Method;
56 import org.apache.xml.serialize.OutputFormat;
57 import org.apache.xml.serialize.Serializer;
58 import org.apache.xml.serialize.SerializerFactory;
59 import org.eclipse.core.resources.ICommand;
60 import org.eclipse.core.resources.IFile;
61 import org.eclipse.core.resources.IFolder;
62 import org.eclipse.core.resources.IMarker;
63 import org.eclipse.core.resources.IProject;
64 import org.eclipse.core.resources.IProjectDescription;
65 import org.eclipse.core.resources.IProjectNature;
66 import org.eclipse.core.resources.IResource;
67 import org.eclipse.core.resources.IWorkspace;
68 import org.eclipse.core.resources.IWorkspaceRoot;
69 import org.eclipse.core.resources.ResourcesPlugin;
70 import org.eclipse.core.runtime.CoreException;
71 import org.eclipse.core.runtime.IPath;
72 import org.eclipse.core.runtime.IProgressMonitor;
73 import org.eclipse.core.runtime.Path;
74 import org.eclipse.core.runtime.Preferences;
75 import org.eclipse.core.runtime.QualifiedName;
76 import org.w3c.dom.Document;
77 import org.w3c.dom.Element;
78 import org.w3c.dom.Node;
79 import org.w3c.dom.NodeList;
80 import org.xml.sax.Attributes;
81 import org.xml.sax.ContentHandler;
82 import org.xml.sax.InputSource;
83 import org.xml.sax.Locator;
84 import org.xml.sax.SAXException;
85 import org.xml.sax.XMLReader;
86
87 /**
88  * Handle for a Java Project.
89  *
90  * <p>A Java Project internally maintains a devpath that corresponds
91  * to the project's classpath. The classpath may include source folders
92  * from the current project; jars in the current project, other projects,
93  * and the local file system; and binary folders (output location) of other
94  * projects. The Java Model presents source elements corresponding to output
95  * .class files in other projects, and thus uses the devpath rather than
96  * the classpath (which is really a compilation path). The devpath mimics
97  * the classpath, except has source folder entries in place of output
98  * locations in external projects.
99  *
100  * <p>Each JavaProject has a NameLookup facility that locates elements
101  * on by name, based on the devpath.
102  *
103  * @see IJavaProject
104  */
105 public class JavaProject 
106         extends Openable 
107         implements IJavaProject , IProjectNature {
108
109         /**
110          * Whether the underlying file system is case sensitive.
111          */
112         protected static final boolean IS_CASE_SENSITIVE = !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
113
114         /**
115          * An empty array of strings indicating that a project doesn't have any prerequesite projects.
116          */
117         protected static final String[] NO_PREREQUISITES = new String[0];
118
119         /**
120          * The platform project this <code>IJavaProject</code> is based on
121          */
122         protected IProject fProject;
123         protected List fLoadPathEntries;
124         protected boolean fScratched;
125         
126         /**
127          * Name of file containing project classpath
128          */
129         public static final String CLASSPATH_FILENAME = ".classpath";  //$NON-NLS-1$
130
131         /**
132          * Name of file containing custom project preferences
133          */
134         public static final String PREF_FILENAME = ".jprefs";  //$NON-NLS-1$
135         
136         /**
137          * Value of the project's raw classpath if the .classpath file contains invalid entries.
138          */
139         public static final IClasspathEntry[] INVALID_CLASSPATH = new IClasspathEntry[0];
140
141         private static final String CUSTOM_DEFAULT_OPTION_VALUE = "#\r\n\r#custom-non-empty-default-value#\r\n\r#"; //$NON-NLS-1$
142         
143         /**
144          * Returns a canonicalized path from the given external path.
145          * Note that the return path contains the same number of segments
146          * and it contains a device only if the given path contained one.
147          * @see java.io.File for the definition of a canonicalized path
148          */
149         public static IPath canonicalizedPath(IPath externalPath) {
150                 
151                 if (externalPath == null)
152                         return null;
153
154 //              if (JavaModelManager.VERBOSE) {
155 //                      System.out.println("JAVA MODEL - Canonicalizing " + externalPath.toString()); //$NON-NLS-1$
156 //              }
157
158                 if (IS_CASE_SENSITIVE) {
159 //                      if (JavaModelManager.VERBOSE) {
160 //                              System.out.println("JAVA MODEL - Canonical path is original path (file system is case sensitive)"); //$NON-NLS-1$
161 //                      }
162                         return externalPath;
163                 }
164
165                 // if not external path, return original path
166                 IWorkspace workspace = ResourcesPlugin.getWorkspace();
167                 if (workspace == null) return externalPath; // protection during shutdown (30487)
168                 if (workspace.getRoot().findMember(externalPath) != null) {
169 //                      if (JavaModelManager.VERBOSE) {
170 //                              System.out.println("JAVA MODEL - Canonical path is original path (member of workspace)"); //$NON-NLS-1$
171 //                      }
172                         return externalPath;
173                 }
174
175                 IPath canonicalPath = null;
176                 try {
177                         canonicalPath =
178                                 new Path(new File(externalPath.toOSString()).getCanonicalPath());
179                 } catch (IOException e) {
180                         // default to original path
181 //                      if (JavaModelManager.VERBOSE) {
182 //                              System.out.println("JAVA MODEL - Canonical path is original path (IOException)"); //$NON-NLS-1$
183 //                      }
184                         return externalPath;
185                 }
186                 
187                 IPath result;
188                 int canonicalLength = canonicalPath.segmentCount();
189                 if (canonicalLength == 0) {
190                         // the java.io.File canonicalization failed
191 //                      if (JavaModelManager.VERBOSE) {
192 //                              System.out.println("JAVA MODEL - Canonical path is original path (canonical path is empty)"); //$NON-NLS-1$
193 //                      }
194                         return externalPath;
195                 } else if (externalPath.isAbsolute()) {
196                         result = canonicalPath;
197                 } else {
198                         // if path is relative, remove the first segments that were added by the java.io.File canonicalization
199                         // e.g. 'lib/classes.zip' was converted to 'd:/myfolder/lib/classes.zip'
200                         int externalLength = externalPath.segmentCount();
201                         if (canonicalLength >= externalLength) {
202                                 result = canonicalPath.removeFirstSegments(canonicalLength - externalLength);
203                         } else {
204 //                              if (JavaModelManager.VERBOSE) {
205 //                                      System.out.println("JAVA MODEL - Canonical path is original path (canonical path is " + canonicalPath.toString() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
206 //                              }
207                                 return externalPath;
208                         }
209                 }
210                 
211                 // keep device only if it was specified (this is because File.getCanonicalPath() converts '/lib/classed.zip' to 'd:/lib/classes/zip')
212                 if (externalPath.getDevice() == null) {
213                         result = result.setDevice(null);
214                 } 
215 //              if (JavaModelManager.VERBOSE) {
216 //                      System.out.println("JAVA MODEL - Canonical path is " + result.toString()); //$NON-NLS-1$
217 //              }
218                 return result;
219         }
220
221         /**
222          * Constructor needed for <code>IProject.getNature()</code> and <code>IProject.addNature()</code>.
223          *
224          * @see #setProject
225          */
226         public JavaProject() {
227                 super(JAVA_PROJECT, null, null);
228         }
229
230         public JavaProject(IProject project, IJavaElement parent) {
231                 super(JAVA_PROJECT, parent, project.getName());
232                 fProject = project;
233         }
234         public void addLoadPathEntry(IProject anotherPHPProject) {
235                 fScratched = true;
236
237                 LoadPathEntry newEntry = new LoadPathEntry(anotherPHPProject);
238                 getLoadPathEntries().add(newEntry);
239         }
240
241         public void configure() throws CoreException {
242                 //      get project description and then the associated build commands 
243                 IProjectDescription desc = fProject.getDescription();
244                 ICommand[] commands = desc.getBuildSpec();
245
246                 // determine if builder already associated
247                 boolean found = false;
248                 for (int i = 0; i < commands.length; ++i) {
249                         if (commands[i].getBuilderName().equals(PHPeclipsePlugin.BUILDER_PARSER_ID)) {
250                                 found = true;
251                                 break;
252                         }
253                 }
254
255                 // add builder if not already in project
256                 if (!found) {
257                         ICommand command = desc.newCommand();
258                         command.setBuilderName(PHPeclipsePlugin.BUILDER_PARSER_ID);
259                         ICommand[] newCommands = new ICommand[commands.length + 1];
260
261                         // Add it before other builders. 
262                         System.arraycopy(commands, 0, newCommands, 1, commands.length);
263                         newCommands[0] = command;
264                         desc.setBuildSpec(newCommands);
265                         fProject.setDescription(desc, null);
266                 }
267         }
268
269         protected void loadLoadPathEntries() {
270                         fLoadPathEntries = new ArrayList();
271
272                         IFile loadPathsFile = getLoadPathEntriesFile();
273
274                         XMLReader reader = null;
275                         try {
276                                 reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
277                                 reader.setContentHandler(getLoadPathEntriesContentHandler());
278                                 reader.parse(new InputSource(loadPathsFile.getContents()));
279                         } catch (Exception e) {
280                                 //the file is nonextant or unreadable
281                         }
282                 }
283                 
284         public List getLoadPathEntries() {
285                 if (fLoadPathEntries == null) {
286                         loadLoadPathEntries();
287                 }
288
289                 return fLoadPathEntries;
290         }
291
292         protected ContentHandler getLoadPathEntriesContentHandler() {
293                 return new ContentHandler() {
294                         public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
295                         }
296
297                         public void endDocument() throws SAXException {
298                         }
299
300                         public void endElement(String arg0, String arg1, String arg2) throws SAXException {
301                         }
302
303                         public void endPrefixMapping(String arg0) throws SAXException {
304                         }
305
306                         public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
307                         }
308
309                         public void processingInstruction(String arg0, String arg1) throws SAXException {
310                         }
311
312                         public void setDocumentLocator(Locator arg0) {
313                         }
314
315                         public void skippedEntity(String arg0) throws SAXException {
316                         }
317
318                         public void startDocument() throws SAXException {
319                         }
320
321                         public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
322                                 if ("pathentry".equals(qName))
323                                         if ("project".equals(atts.getValue("type"))) {
324                                                 IPath referencedProjectPath = new Path(atts.getValue("path"));
325                                                 IProject referencedProject = getProject(referencedProjectPath.lastSegment());
326                                                 fLoadPathEntries.add(new LoadPathEntry(referencedProject));
327                                         }
328                         }
329
330                         public void startPrefixMapping(String arg0, String arg1) throws SAXException {
331                         }
332                 };
333         }
334
335         protected IFile getLoadPathEntriesFile() {
336                 return fProject.getFile(".loadpath");
337         }
338
339         protected String getLoadPathXML() {
340                 StringBuffer buffer = new StringBuffer();
341                 buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><loadpath>");
342
343                 Iterator pathEntriesIterator = fLoadPathEntries.iterator();
344
345                 while (pathEntriesIterator.hasNext()) {
346                         LoadPathEntry entry = (LoadPathEntry) pathEntriesIterator.next();
347                         buffer.append(entry.toXML());
348                 }
349
350                 buffer.append("</loadpath>");
351                 return buffer.toString();
352         }
353         /**
354          * Adds a builder to the build spec for the given project.
355          */
356         protected void addToBuildSpec(String builderID) throws CoreException {
357
358                 IProjectDescription description = getProject().getDescription();
359                 ICommand javaCommand = getJavaCommand(description);
360
361                 if (javaCommand == null) {
362
363                         // Add a Java command to the build spec
364                         ICommand command = description.newCommand();
365                         command.setBuilderName(builderID);
366                         setJavaCommand(description, command);
367                 }
368         }
369
370         protected void closing(Object info) throws JavaModelException {
371                 
372                 // forget source attachment recommendations
373                 IPackageFragmentRoot[] roots = this.getPackageFragmentRoots();
374 //              for (int i = 0; i < roots.length; i++) {
375 //                      if (roots[i] instanceof JarPackageFragmentRoot){
376 //                              ((JarPackageFragmentRoot) roots[i]).setSourceAttachmentProperty(null); 
377 //                      }
378 //              }
379                 
380                 super.closing(info);
381         }
382         
383
384
385         /**
386          * Internal computation of an expanded classpath. It will eliminate duplicates, and produce copies
387          * of exported classpath entries to avoid possible side-effects ever after.
388          */                     
389         private void computeExpandedClasspath(
390                 JavaProject initialProject, 
391                 boolean ignoreUnresolvedVariable,
392                 boolean generateMarkerOnError,
393                 HashSet visitedProjects, 
394                 ObjectVector accumulatedEntries) throws JavaModelException {
395                 
396                 if (visitedProjects.contains(this)){
397                         return; // break cycles if any
398                 }
399                 visitedProjects.add(this);
400
401                 if (generateMarkerOnError && !this.equals(initialProject)){
402                         generateMarkerOnError = false;
403                 }
404                 IClasspathEntry[] immediateClasspath = 
405                         getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError);
406                         
407                 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
408                 for (int i = 0, length = immediateClasspath.length; i < length; i++){
409                         IClasspathEntry entry = immediateClasspath[i];
410
411                         boolean isInitialProject = this.equals(initialProject);
412                         if (isInitialProject || entry.isExported()){
413                                 
414                                 accumulatedEntries.add(entry);
415                                 
416                                 // recurse in project to get all its indirect exports (only consider exported entries from there on)                            
417                                 if (entry.getEntryKind() == ClasspathEntry.CPE_PROJECT) {
418                                         IResource member = workspaceRoot.findMember(entry.getPath()); 
419                                         if (member != null && member.getType() == IResource.PROJECT){ // double check if bound to project (23977)
420                                                 IProject projRsc = (IProject) member;
421                                                 if (JavaProject.hasJavaNature(projRsc)) {                               
422                                                         JavaProject project = (JavaProject) PHPCore.create(projRsc);
423                                                         project.computeExpandedClasspath(
424                                                                 initialProject, 
425                                                                 ignoreUnresolvedVariable, 
426                                                                 generateMarkerOnError,
427                                                                 visitedProjects, 
428                                                                 accumulatedEntries);
429                                                 }
430                                         }
431                                 }
432                         }                       
433                 }
434         }
435         
436         /**
437          * Returns (local/all) the package fragment roots identified by the given project's classpath.
438          * Note: this follows project classpath references to find required project contributions,
439          * eliminating duplicates silently.
440          * Only works with resolved entries
441          */
442         public IPackageFragmentRoot[] computePackageFragmentRoots(IClasspathEntry[] resolvedClasspath, boolean retrieveExportedRoots) throws JavaModelException {
443
444                 ObjectVector accumulatedRoots = new ObjectVector();
445                 computePackageFragmentRoots(
446                         resolvedClasspath, 
447                         accumulatedRoots, 
448                         new HashSet(5), // rootIDs
449                         true, // inside original project
450                         true, // check existency
451                         retrieveExportedRoots);
452                 IPackageFragmentRoot[] rootArray = new IPackageFragmentRoot[accumulatedRoots.size()];
453                 accumulatedRoots.copyInto(rootArray);
454                 return rootArray;
455         }
456
457         /**
458          * Computes the package fragment roots identified by the given entry.
459          * Only works with resolved entry
460          */
461         public IPackageFragmentRoot[] computePackageFragmentRoots(IClasspathEntry resolvedEntry) {
462                 try {
463                         return 
464                                 computePackageFragmentRoots(
465                                         new IClasspathEntry[]{ resolvedEntry }, 
466                                         false // don't retrieve exported roots
467                                 );
468                 } catch (JavaModelException e) {
469                         return new IPackageFragmentRoot[] {};
470                 }
471         }
472         
473         /**
474          * Returns the package fragment roots identified by the given entry. In case it refers to
475          * a project, it will follow its classpath so as to find exported roots as well.
476          * Only works with resolved entry
477          */
478         public void computePackageFragmentRoots(
479                 IClasspathEntry resolvedEntry,
480                 ObjectVector accumulatedRoots, 
481                 HashSet rootIDs, 
482                 boolean insideOriginalProject,
483                 boolean checkExistency,
484                 boolean retrieveExportedRoots) throws JavaModelException {
485                         
486                 String rootID = ((ClasspathEntry)resolvedEntry).rootID();
487                 if (rootIDs.contains(rootID)) return;
488
489                 IPath projectPath = getProject().getFullPath();
490                 IPath entryPath = resolvedEntry.getPath();
491                 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
492                 
493                 switch(resolvedEntry.getEntryKind()){
494                         
495                         // source folder
496                         case IClasspathEntry.CPE_SOURCE :
497
498                                 if (projectPath.isPrefixOf(entryPath)){
499                                         if (checkExistency) {
500                                                 Object target = JavaModel.getTarget(workspaceRoot, entryPath, checkExistency);
501                                                 if (target == null) return;
502         
503                                                 if (target instanceof IFolder || target instanceof IProject){
504                                                         accumulatedRoots.add(
505                                                                 getPackageFragmentRoot((IResource)target));
506                                                         rootIDs.add(rootID);
507                                                 }
508                                         } else {
509                                                 IPackageFragmentRoot root = getFolderPackageFragmentRoot(entryPath);
510                                                 if (root != null) {
511                                                         accumulatedRoots.add(root);
512                                                         rootIDs.add(rootID);
513                                                 }
514                                         }
515                                 }
516                                 break;
517
518                         // internal/external JAR or folder
519                         case IClasspathEntry.CPE_LIBRARY :
520                         
521                                 if (!insideOriginalProject && !resolvedEntry.isExported()) return;
522                                 
523                                 if (checkExistency) {
524                                         Object target = JavaModel.getTarget(workspaceRoot, entryPath, checkExistency);
525                                         if (target == null) return;
526         
527                                         if (target instanceof IResource){
528                                                 // internal target
529                                                 IResource resource = (IResource) target;
530                                                 IPackageFragmentRoot root = getPackageFragmentRoot(resource);
531                                                 if (root != null) {
532                                                         accumulatedRoots.add(root);
533                                                         rootIDs.add(rootID);
534                                                 }
535                                         } else {
536                                                 // external target - only JARs allowed
537 //                                              if (((java.io.File)target).isFile() && (Util.isArchiveFileName(entryPath.lastSegment()))) {
538 //                                                      accumulatedRoots.add(
539 //                                                              new JarPackageFragmentRoot(entryPath, this));
540 //                                                      rootIDs.add(rootID);
541 //                                              }
542                                         }
543                                 } else {
544                                         IPackageFragmentRoot root = getPackageFragmentRoot(entryPath);
545                                         if (root != null) {
546                                                 accumulatedRoots.add(root);
547                                                 rootIDs.add(rootID);
548                                         }
549                                 }
550                                 break;
551
552                         // recurse into required project
553                         case IClasspathEntry.CPE_PROJECT :
554
555                                 if (!retrieveExportedRoots) return;
556                                 if (!insideOriginalProject && !resolvedEntry.isExported()) return;
557
558                                 IResource member = workspaceRoot.findMember(entryPath);
559                                 if (member != null && member.getType() == IResource.PROJECT){// double check if bound to project (23977)
560                                         IProject requiredProjectRsc = (IProject) member;
561                                         if (JavaProject.hasJavaNature(requiredProjectRsc)){ // special builder binary output
562                                                 rootIDs.add(rootID);
563                                                 JavaProject requiredProject = (JavaProject)PHPCore.create(requiredProjectRsc);
564                                                 requiredProject.computePackageFragmentRoots(
565                                                         requiredProject.getResolvedClasspath(true), 
566                                                         accumulatedRoots, 
567                                                         rootIDs, 
568                                                         false, 
569                                                         checkExistency, 
570                                                         retrieveExportedRoots);
571                                         }
572                                 break;
573                         }
574                 }
575         }
576
577         /**
578          * Returns (local/all) the package fragment roots identified by the given project's classpath.
579          * Note: this follows project classpath references to find required project contributions,
580          * eliminating duplicates silently.
581          * Only works with resolved entries
582          */
583         public void computePackageFragmentRoots(
584                 IClasspathEntry[] resolvedClasspath,
585                 ObjectVector accumulatedRoots, 
586                 HashSet rootIDs, 
587                 boolean insideOriginalProject,
588                 boolean checkExistency,
589                 boolean retrieveExportedRoots) throws JavaModelException {
590
591                 if (insideOriginalProject){
592                         rootIDs.add(rootID());
593                 }       
594                 for (int i = 0, length = resolvedClasspath.length; i < length; i++){
595                         computePackageFragmentRoots(
596                                 resolvedClasspath[i],
597                                 accumulatedRoots,
598                                 rootIDs,
599                                 insideOriginalProject,
600                                 checkExistency,
601                                 retrieveExportedRoots);
602                 }
603         }
604
605         /**
606          * Compute the file name to use for a given shared property
607          */
608         public String computeSharedPropertyFileName(QualifiedName qName) {
609
610                 return '.' + qName.getLocalName();
611         }
612         
613         /*
614          * Returns whether the given resource is accessible through the children or the non-Java resources of this project.
615          * Returns true if the resource is not in the project.
616          * Assumes that the resource is a folder or a file.
617          */
618         public boolean contains(IResource resource) {
619                         
620                 IClasspathEntry[] classpath;
621                 IPath output;
622                 try {
623                         classpath = getResolvedClasspath(true);
624                         output = getOutputLocation();
625                 } catch (JavaModelException e) {
626                         return false;
627                 }
628                 
629                 IPath fullPath = resource.getFullPath();
630                 IPath innerMostOutput = output.isPrefixOf(fullPath) ? output : null;
631                 IClasspathEntry innerMostEntry = null;
632                 for (int j = 0, cpLength = classpath.length; j < cpLength; j++) {
633                         IClasspathEntry entry = classpath[j];
634                 
635                         IPath entryPath = entry.getPath();
636                         if ((innerMostEntry == null || innerMostEntry.getPath().isPrefixOf(entryPath))
637                                         && entryPath.isPrefixOf(fullPath)) {
638                                 innerMostEntry = entry;
639                         }
640                         IPath entryOutput = classpath[j].getOutputLocation();
641                         if (entryOutput != null && entryOutput.isPrefixOf(fullPath)) {
642                                 innerMostOutput = entryOutput;
643                         }
644                 }
645                 if (innerMostEntry != null) {
646                         // special case prj==src and nested output location
647                         if (innerMostOutput != null && innerMostOutput.segmentCount() > 1 // output isn't project
648                                         && innerMostEntry.getPath().segmentCount() == 1) { // 1 segment must be project name
649                                 return false;
650                         }
651                         if  (resource instanceof IFolder) {
652                                  // folders are always included in src/lib entries
653                                  return true;
654                         }
655                         switch (innerMostEntry.getEntryKind()) {
656                                 case IClasspathEntry.CPE_SOURCE:
657                                         // .class files are not visible in source folders 
658                                         return true; //!Util.isClassFileName(fullPath.lastSegment());
659                                 case IClasspathEntry.CPE_LIBRARY:
660                                         // .java files are not visible in library folders
661                                         return !Util.isJavaFileName(fullPath.lastSegment());
662                         }
663                 }
664                 if (innerMostOutput != null) {
665                         return false;
666                 }
667                 return true;
668         }
669
670         /**
671          * Record a new marker denoting a classpath problem
672          */
673         IMarker createClasspathProblemMarker(IJavaModelStatus status) {
674                         
675                 IMarker marker = null;
676                 int severity;
677                 String[] arguments = new String[0];
678                 boolean isCycleProblem = false, isClasspathFileFormatProblem = false;
679                 switch (status.getCode()) {
680
681                         case  IJavaModelStatusConstants.CLASSPATH_CYCLE :
682                                 isCycleProblem = true;
683                                 if (PHPCore.ERROR.equals(getOption(PHPCore.CORE_CIRCULAR_CLASSPATH, true))) {
684                                         severity = IMarker.SEVERITY_ERROR;
685                                 } else {
686                                         severity = IMarker.SEVERITY_WARNING;
687                                 }
688                                 break;
689
690                         case  IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT :
691                                 isClasspathFileFormatProblem = true;
692                                 severity = IMarker.SEVERITY_ERROR;
693                                 break;
694
695                         default:
696                                 IPath path = status.getPath();
697                                 if (path != null) arguments = new String[] { path.toString() };
698                                 if (PHPCore.ERROR.equals(getOption(PHPCore.CORE_INCOMPLETE_CLASSPATH, true))) {
699                                         severity = IMarker.SEVERITY_ERROR;
700                                 } else {
701                                         severity = IMarker.SEVERITY_WARNING;
702                                 }
703                                 break;
704                 }
705                 
706                 try {
707                         marker = getProject().createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER);
708                         marker.setAttributes(
709                                 new String[] { 
710                                         IMarker.MESSAGE, 
711                                         IMarker.SEVERITY, 
712                                         IMarker.LOCATION, 
713                                         IJavaModelMarker.CYCLE_DETECTED,
714                                         IJavaModelMarker.CLASSPATH_FILE_FORMAT,
715                                         IJavaModelMarker.ID,
716                                         IJavaModelMarker.ARGUMENTS ,
717                                 },
718                                 new Object[] {
719                                         status.getMessage(),
720                                         new Integer(severity), 
721                                         Util.bind("classpath.buildPath"),//$NON-NLS-1$
722                                         isCycleProblem ? "true" : "false",//$NON-NLS-1$ //$NON-NLS-2$
723                                         isClasspathFileFormatProblem ? "true" : "false",//$NON-NLS-1$ //$NON-NLS-2$
724                                         new Integer(status.getCode()),
725                                         Util.getProblemArgumentsForMarker(arguments) ,
726                                 }
727                         );
728                 } catch (CoreException e) {
729                 }
730                 return marker;
731         }
732         
733         /**
734          * Returns a new element info for this element.
735          */
736         protected OpenableElementInfo createElementInfo() {
737
738                 return new JavaProjectElementInfo();
739         }
740
741         /**
742          * Reads and decode an XML classpath string
743          */
744         protected IClasspathEntry[] decodeClasspath(String xmlClasspath, boolean createMarker, boolean logProblems) {
745
746                 ArrayList paths = new ArrayList();
747                 IClasspathEntry defaultOutput = null;
748                 try {
749                         if (xmlClasspath == null) return null;
750                         StringReader reader = new StringReader(xmlClasspath);
751                         Element cpElement;
752         
753                         try {
754                                 DocumentBuilder parser =
755                                         DocumentBuilderFactory.newInstance().newDocumentBuilder();
756                                 cpElement = parser.parse(new InputSource(reader)).getDocumentElement();
757                         } catch (SAXException e) {
758                                 throw new IOException(Util.bind("file.badFormat")); //$NON-NLS-1$
759                         } catch (ParserConfigurationException e) {
760                                 throw new IOException(Util.bind("file.badFormat")); //$NON-NLS-1$
761                         } finally {
762                                 reader.close();
763                         }
764         
765                         if (!cpElement.getNodeName().equalsIgnoreCase("classpath")) { //$NON-NLS-1$
766                                 throw new IOException(Util.bind("file.badFormat")); //$NON-NLS-1$
767                         }
768                         NodeList list = cpElement.getElementsByTagName("classpathentry"); //$NON-NLS-1$
769                         int length = list.getLength();
770         
771                         for (int i = 0; i < length; ++i) {
772                                 Node node = list.item(i);
773                                 if (node.getNodeType() == Node.ELEMENT_NODE) {
774                                         IClasspathEntry entry = ClasspathEntry.elementDecode((Element)node, this);
775                                         if (entry != null){
776                                                 if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) { 
777                                                         defaultOutput = entry; // separate output
778                                                 } else {
779                                                         paths.add(entry);
780                                 }
781                         }
782                                 }
783                         }
784                 } catch (IOException e) {
785                         // bad format
786                         if (createMarker && this.getProject().isAccessible()) {
787                                         this.createClasspathProblemMarker(new JavaModelStatus(
788                                                         IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
789                                                         Util.bind("classpath.xmlFormatError", this.getElementName(), e.getMessage()))); //$NON-NLS-1$
790                         }
791                         if (logProblems) {
792                                 Util.log(e, 
793                                         "Exception while retrieving "+ this.getPath() //$NON-NLS-1$
794                                         +"/.classpath, will mark classpath as invalid"); //$NON-NLS-1$
795                         }
796                         return INVALID_CLASSPATH;
797                 } catch (Assert.AssertionFailedException e) { 
798                         // failed creating CP entries from file
799                         if (createMarker && this.getProject().isAccessible()) {
800                                 this.createClasspathProblemMarker(new JavaModelStatus(
801                                                 IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
802                                                 Util.bind("classpath.illegalEntryInClasspathFile", this.getElementName(), e.getMessage()))); //$NON-NLS-1$
803                         }
804                         if (logProblems) {
805                                 Util.log(e, 
806                                         "Exception while retrieving "+ this.getPath() //$NON-NLS-1$
807                                         +"/.classpath, will mark classpath as invalid"); //$NON-NLS-1$
808                         }
809                         return INVALID_CLASSPATH;
810                 }
811                 int pathSize = paths.size();
812                 if (pathSize > 0 || defaultOutput != null) {
813                         IClasspathEntry[] entries = new IClasspathEntry[pathSize + (defaultOutput == null ? 0 : 1)];
814                         paths.toArray(entries);
815                         if (defaultOutput != null) entries[pathSize] = defaultOutput; // ensure output is last item
816                         return entries;
817                 } else {
818                         return null;
819                 }
820         }
821
822         /**
823         /**
824          * Removes the Java nature from the project.
825          */
826         public void deconfigure() throws CoreException {
827
828                 // deregister Java builder
829                 removeFromBuildSpec(PHPeclipsePlugin.BUILDER_PARSER_ID);
830         }
831
832         /**
833          * Returns a default class path.
834          * This is the root of the project
835          */
836         protected IClasspathEntry[] defaultClasspath() throws JavaModelException {
837
838                 return new IClasspathEntry[] {
839                          PHPCore.newSourceEntry(getProject().getFullPath())};
840         }
841
842         /**
843          * Returns a default output location.
844          * This is the project bin folder
845          */
846         protected IPath defaultOutputLocation() throws JavaModelException {
847                 return getProject().getFullPath().append("bin"); //$NON-NLS-1$
848         }
849
850         /**
851          * Returns the XML String encoding of the class path.
852          */
853         protected String encodeClasspath(IClasspathEntry[] classpath, IPath outputLocation, boolean useLineSeparator) throws JavaModelException {
854
855                 Document document = new DocumentImpl();
856                 Element cpElement = document.createElement("classpath"); //$NON-NLS-1$
857                 document.appendChild(cpElement);
858
859                 for (int i = 0; i < classpath.length; ++i) {
860                         cpElement.appendChild(((ClasspathEntry)classpath[i]).elementEncode(document, getProject().getFullPath()));
861                 }
862
863                 if (outputLocation != null) {
864                         outputLocation = outputLocation.removeFirstSegments(1);
865                         outputLocation = outputLocation.makeRelative();
866                         Element oElement = document.createElement("classpathentry"); //$NON-NLS-1$
867                         oElement.setAttribute("kind", ClasspathEntry.kindToString(ClasspathEntry.K_OUTPUT));    //$NON-NLS-1$
868                         oElement.setAttribute("path", outputLocation.toString()); //$NON-NLS-1$
869                         cpElement.appendChild(oElement);
870                 }
871
872                 // produce a String output
873                 try {
874                         ByteArrayOutputStream s = new ByteArrayOutputStream();
875                         OutputFormat format = new OutputFormat();
876                         if (useLineSeparator) {
877                                 format.setIndenting(true);
878                                 format.setLineSeparator(System.getProperty("line.separator"));  //$NON-NLS-1$
879                         } else {
880                                 format.setPreserveSpace(true);
881                         }                       
882                         Serializer serializer =
883                                 SerializerFactory.getSerializerFactory(Method.XML).makeSerializer(
884                                         new OutputStreamWriter(s, "UTF8"), //$NON-NLS-1$
885                                         format);
886                         serializer.asDOMSerializer().serialize(document);
887                         return s.toString("UTF8"); //$NON-NLS-1$
888                 } catch (IOException e) {
889                         throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
890                 }
891         }
892         
893         /**
894          * Returns true if this handle represents the same Java project
895          * as the given handle. Two handles represent the same
896          * project if they are identical or if they represent a project with 
897          * the same underlying resource and occurrence counts.
898          *
899          * @see JavaElement#equals
900          */
901         public boolean equals(Object o) {
902
903                 if (this == o)
904                         return true;
905
906                 if (!(o instanceof JavaProject))
907                         return false;
908
909                 JavaProject other = (JavaProject) o;
910                 return getProject().equals(other.getProject())
911                         && fOccurrenceCount == other.fOccurrenceCount;
912         }
913
914         public boolean exists() {
915                 if (!hasJavaNature(fProject)) return false;
916                 return super.exists();
917         }       
918
919         /**
920          * @see IJavaProject
921          */
922         public IJavaElement findElement(IPath path) throws JavaModelException {
923
924                 if (path == null || path.isAbsolute()) {
925                         throw new JavaModelException(
926                                 new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, path));
927                 }
928 //              try {
929
930                         String extension = path.getFileExtension();
931                         if (extension == null) {
932                                 String packageName = path.toString().replace(IPath.SEPARATOR, '.');
933
934 //                              IPackageFragment[] pkgFragments =
935 //                                      getNameLookup().findPackageFragments(packageName, false);
936 //                              if (pkgFragments == null) {
937                                         return null;
938
939 //                              } else {
940 //                                      // try to return one that is a child of this project
941 //                                      for (int i = 0, length = pkgFragments.length; i < length; i++) {
942 //
943 //                                              IPackageFragment pkgFragment = pkgFragments[i];
944 //                                              if (this.equals(pkgFragment.getParent().getParent())) {
945 //                                                      return pkgFragment;
946 //                                              }
947 //                                      }
948 //                                      // default to the first one
949 //                                      return pkgFragments[0];
950 //                              }
951                         } else if (
952                                 extension.equalsIgnoreCase("java") //$NON-NLS-1$
953                                         || extension.equalsIgnoreCase("class")) {  //$NON-NLS-1$
954                                 IPath packagePath = path.removeLastSegments(1);
955                                 String packageName = packagePath.toString().replace(IPath.SEPARATOR, '.');
956                                 String typeName = path.lastSegment();
957                                 typeName = typeName.substring(0, typeName.length() - extension.length() - 1);
958                                 String qualifiedName = null;
959                                 if (packageName.length() > 0) {
960                                         qualifiedName = packageName + "." + typeName; //$NON-NLS-1$
961                                 } else {
962                                         qualifiedName = typeName;
963                                 }
964 //                              IType type =
965 //                                      getNameLookup().findType(
966 //                                              qualifiedName,
967 //                                              false,
968 //                                              NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
969 //                              if (type != null) {
970 //                                      return type.getParent();
971 //                              } else {
972                                         return null;
973 //                              }
974                         } else {
975                                 // unsupported extension
976                                 return null;
977                         }
978 //              } catch (JavaModelException e) {
979 //                      if (e.getStatus().getCode()
980 //                              == IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST) {
981 //                              return null;
982 //                      } else {
983 //                              throw e;
984 //                      }
985 //              }
986         }
987
988         /**
989          * @see IJavaProject
990          */
991 //      public IPackageFragment findPackageFragment(IPath path)
992 //              throws JavaModelException {
993 //
994 //              return findPackageFragment0(JavaProject.canonicalizedPath(path));
995 //      }
996 //
997 //      /**
998 //       * non path canonicalizing version
999 //       */
1000 //      public IPackageFragment findPackageFragment0(IPath path) 
1001 //              throws JavaModelException {
1002 //
1003 //              return getNameLookup().findPackageFragment(path);
1004 //      }
1005
1006         /**
1007          * @see IJavaProject
1008          */
1009         public IPackageFragmentRoot findPackageFragmentRoot(IPath path)
1010                 throws JavaModelException {
1011
1012                 return findPackageFragmentRoot0(JavaProject.canonicalizedPath(path));
1013         }
1014
1015         /**
1016          * no path canonicalization 
1017          */
1018         public IPackageFragmentRoot findPackageFragmentRoot0(IPath path)
1019                 throws JavaModelException {
1020
1021                 IPackageFragmentRoot[] allRoots = this.getAllPackageFragmentRoots();
1022                 if (!path.isAbsolute()) {
1023                         throw new IllegalArgumentException(Util.bind("path.mustBeAbsolute")); //$NON-NLS-1$
1024                 }
1025                 for (int i= 0; i < allRoots.length; i++) {
1026                         IPackageFragmentRoot classpathRoot= allRoots[i];
1027                         if (classpathRoot.getPath().equals(path)) {
1028                                 return classpathRoot;
1029                         }
1030                 }
1031                 return null;
1032         }
1033         /**
1034          * @see IJavaProject
1035          */
1036         public IPackageFragmentRoot[] findPackageFragmentRoots(IClasspathEntry entry) {
1037                 try {
1038                         IClasspathEntry[] classpath = this.getRawClasspath();
1039                         for (int i = 0, length = classpath.length; i < length; i++) {
1040                                 if (classpath[i].equals(entry)) { // entry may need to be resolved
1041                                         return 
1042                                                 computePackageFragmentRoots(
1043                                                         getResolvedClasspath(new IClasspathEntry[] {entry}, null, true, false, null/*no reverse map*/), 
1044                                                         false); // don't retrieve exported roots
1045                                 }
1046                         }
1047                 } catch (JavaModelException e) {
1048                 }
1049                 return new IPackageFragmentRoot[] {};
1050         }
1051         
1052         /**
1053          * @see IJavaProject#findType(String)
1054          */
1055 //      public IType findType(String fullyQualifiedName) throws JavaModelException {
1056 //              IType type = 
1057 //                      this.getNameLookup().findType(
1058 //                              fullyQualifiedName, 
1059 //                              false,
1060 //                              NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
1061 //              if (type == null) {
1062 //                      // try to find enclosing type
1063 //                      int lastDot = fullyQualifiedName.lastIndexOf('.');
1064 //                      if (lastDot == -1) return null;
1065 //                      type = this.findType(fullyQualifiedName.substring(0, lastDot));
1066 //                      if (type != null) {
1067 //                              type = type.getType(fullyQualifiedName.substring(lastDot+1));
1068 //                              if (!type.exists()) {
1069 //                                      return null;
1070 //                              }
1071 //                      }
1072 //              }
1073 //              return type;
1074 //      }
1075         
1076         /**
1077          * @see IJavaProject#findType(String, String)
1078          */
1079 //      public IType findType(String packageName, String typeQualifiedName) throws JavaModelException {
1080 //              return 
1081 //                      this.getNameLookup().findType(
1082 //                              typeQualifiedName, 
1083 //                              packageName,
1084 //                              false,
1085 //                              NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
1086 //      }       
1087 //      
1088         /**
1089          * Remove all markers denoting classpath problems
1090          */
1091         protected void flushClasspathProblemMarkers(boolean flushCycleMarkers, boolean flushClasspathFormatMarkers) {
1092                 try {
1093                         IProject project = getProject();
1094                         if (project.exists()) {
1095                                 IMarker[] markers = project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
1096                                 for (int i = 0, length = markers.length; i < length; i++) {
1097                                         IMarker marker = markers[i];
1098                                         if (flushCycleMarkers && flushClasspathFormatMarkers) {
1099                                                 marker.delete();
1100                                         } else {
1101                                                 String cycleAttr = (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
1102                                                 String classpathFileFormatAttr =  (String)marker.getAttribute(IJavaModelMarker.CLASSPATH_FILE_FORMAT);
1103                                                 if ((flushCycleMarkers == (cycleAttr != null && cycleAttr.equals("true"))) //$NON-NLS-1$
1104                                                         && (flushClasspathFormatMarkers == (classpathFileFormatAttr != null && classpathFileFormatAttr.equals("true")))){ //$NON-NLS-1$
1105                                                         marker.delete();
1106                                                 }
1107                                         }
1108                                 }
1109                         }
1110                 } catch (CoreException e) {
1111                 }
1112         }
1113
1114         /**
1115          * @see Openable
1116          */
1117         protected boolean generateInfos(
1118                 OpenableElementInfo info,
1119                 IProgressMonitor pm,
1120                 Map newElements,
1121                 IResource underlyingResource) throws JavaModelException {
1122
1123                 boolean validInfo = false;
1124                 try {
1125                         if (getProject().isOpen()) {
1126                                 // put the info now, because computing the roots requires it
1127                                 JavaModelManager.getJavaModelManager().putInfo(this, info);
1128
1129                                 // compute the pkg fragment roots
1130                                 updatePackageFragmentRoots();                           
1131         
1132                                 // remember the timestamps of external libraries the first time they are looked up
1133                                 IClasspathEntry[] resolvedClasspath = getResolvedClasspath(true/*ignore unresolved variable*/);
1134                                 for (int i = 0, length = resolvedClasspath.length; i < length; i++) {
1135                                         IClasspathEntry entry = resolvedClasspath[i];
1136                                         if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
1137                                                 IPath path = entry.getPath();
1138                                                 Object target = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), path, true);
1139                                                 if (target instanceof java.io.File) {
1140                                                         Map externalTimeStamps = JavaModelManager.getJavaModelManager().deltaProcessor.externalTimeStamps;
1141                                                         if (externalTimeStamps.get(path) == null) {
1142                                                                 long timestamp = DeltaProcessor.getTimeStamp((java.io.File)target);
1143                                                                 externalTimeStamps.put(path, new Long(timestamp));                                                      
1144                                                         }
1145                                                 }
1146                                         }
1147                                 }                       
1148
1149                                 // only valid if reaches here                           
1150                                 validInfo = true;
1151                         }
1152                 } finally {
1153                         if (!validInfo)
1154                                 JavaModelManager.getJavaModelManager().removeInfo(this);
1155                 }
1156                 return validInfo;
1157         }
1158
1159         /**
1160          * @see IJavaProject
1161          */
1162         public IPackageFragmentRoot[] getAllPackageFragmentRoots()
1163                 throws JavaModelException {
1164
1165                 return computePackageFragmentRoots(getResolvedClasspath(true), true);
1166         }
1167
1168         /**
1169          * Returns the classpath entry that refers to the given path
1170          * or <code>null</code> if there is no reference to the path.
1171          */
1172         public IClasspathEntry getClasspathEntryFor(IPath path)
1173                 throws JavaModelException {
1174
1175                 IClasspathEntry[] entries = getExpandedClasspath(true);
1176                 for (int i = 0; i < entries.length; i++) {
1177                         if (entries[i].getPath().equals(path)) {
1178                                 return entries[i];
1179                         }
1180                 }
1181                 return null;
1182         }
1183
1184         /*
1185          * Returns the cycle marker associated with this project or null if none.
1186          */
1187         public IMarker getCycleMarker(){
1188                 try {
1189                         IProject project = getProject();
1190                         if (project.exists()) {
1191                                 IMarker[] markers = project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
1192                                 for (int i = 0, length = markers.length; i < length; i++) {
1193                                         IMarker marker = markers[i];
1194                                         String cycleAttr = (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
1195                                         if (cycleAttr != null && cycleAttr.equals("true")){ //$NON-NLS-1$
1196                                                 return marker;
1197                                         }
1198                                 }
1199                         }
1200                 } catch (CoreException e) {
1201                 }
1202                 return null;
1203         }
1204
1205         /**
1206          * This is a helper method returning the expanded classpath for the project, as a list of classpath entries, 
1207          * where all classpath variable entries have been resolved and substituted with their final target entries.
1208          * All project exports have been appended to project entries.
1209          */
1210         public IClasspathEntry[] getExpandedClasspath(boolean ignoreUnresolvedVariable) throws JavaModelException {
1211                         
1212                         return getExpandedClasspath(ignoreUnresolvedVariable, false);
1213         }
1214                 
1215         /**
1216          * Internal variant which can create marker on project for invalid entries,
1217          * it will also perform classpath expansion in presence of project prerequisites
1218          * exporting their entries.
1219          */
1220         public IClasspathEntry[] getExpandedClasspath(
1221                 boolean ignoreUnresolvedVariable,
1222                 boolean generateMarkerOnError) throws JavaModelException {
1223         
1224                 ObjectVector accumulatedEntries = new ObjectVector();           
1225                 computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries);
1226                 
1227                 IClasspathEntry[] expandedPath = new IClasspathEntry[accumulatedEntries.size()];
1228                 accumulatedEntries.copyInto(expandedPath);
1229
1230                 return expandedPath;
1231         }
1232
1233         /**
1234          * Returns the <code>char</code> that marks the start of this handles
1235          * contribution to a memento.
1236          */
1237         protected char getHandleMementoDelimiter() {
1238
1239                 return JEM_JAVAPROJECT;
1240         }
1241
1242         /**
1243          * Find the specific Java command amongst the build spec of a given description
1244          */
1245         private ICommand getJavaCommand(IProjectDescription description)
1246                 throws CoreException {
1247
1248                 ICommand[] commands = description.getBuildSpec();
1249                 for (int i = 0; i < commands.length; ++i) {
1250                         if (commands[i].getBuilderName().equals(PHPeclipsePlugin.BUILDER_PARSER_ID)) {
1251                                 return commands[i];
1252                         }
1253                 }
1254                 return null;
1255         }
1256
1257         /**
1258          * Convenience method that returns the specific type of info for a Java project.
1259          */
1260         protected JavaProjectElementInfo getJavaProjectElementInfo()
1261                 throws JavaModelException {
1262
1263                 return (JavaProjectElementInfo) getElementInfo();
1264         }
1265
1266         /**
1267          * @see IJavaProject
1268          */
1269         public NameLookup getNameLookup() throws JavaModelException {
1270
1271                 JavaProjectElementInfo info = getJavaProjectElementInfo();
1272                 // lock on the project info to avoid race condition
1273                 synchronized(info){
1274                         NameLookup nameLookup;
1275                         if ((nameLookup = info.getNameLookup()) == null){
1276                                 info.setNameLookup(nameLookup = new NameLookup(this));
1277                         }
1278                         return nameLookup;
1279                 }
1280         }
1281 //
1282 //      /**
1283 //       * Returns an array of non-java resources contained in the receiver.
1284 //       */
1285 //      public Object[] getNonJavaResources() throws JavaModelException {
1286 //
1287 //              return ((JavaProjectElementInfo) getElementInfo()).getNonJavaResources(this);
1288 //      }
1289
1290         /**
1291          * @see org.eclipse.jdt.core.IJavaProject#getOption(String, boolean)
1292          */     
1293         public String getOption(String optionName, boolean inheritJavaCoreOptions) {
1294                 
1295                 if (JavaModelManager.OptionNames.contains(optionName)){
1296                         
1297                         Preferences preferences = getPreferences();
1298                         if (preferences == null || preferences.isDefault(optionName)) {
1299                                 return inheritJavaCoreOptions ? PHPCore.getOption(optionName) : null;
1300                         }
1301                         return preferences.getString(optionName).trim();
1302                 }
1303                 return null;
1304         }
1305         
1306         /**
1307          * @see org.eclipse.jdt.core.IJavaProject#getOptions(boolean)
1308          */
1309         public Map getOptions(boolean inheritJavaCoreOptions) {
1310                 
1311                 // initialize to the defaults from JavaCore options pool
1312                 Map options = inheritJavaCoreOptions ? PHPCore.getOptions() : new Hashtable(5);
1313
1314                 Preferences preferences = getPreferences();
1315                 if (preferences == null) return options; // cannot do better (non-Java project)
1316                 HashSet optionNames = JavaModelManager.OptionNames;
1317                 
1318                 // get preferences set to their default
1319                 if (inheritJavaCoreOptions){
1320                         String[] defaultPropertyNames = preferences.defaultPropertyNames();
1321                         for (int i = 0; i < defaultPropertyNames.length; i++){
1322                                 String propertyName = defaultPropertyNames[i];
1323                                 if (optionNames.contains(propertyName)){
1324                                         options.put(propertyName, preferences.getDefaultString(propertyName).trim());
1325                                 }
1326                         }               
1327                 }
1328                 // get custom preferences not set to their default
1329                 String[] propertyNames = preferences.propertyNames();
1330                 for (int i = 0; i < propertyNames.length; i++){
1331                         String propertyName = propertyNames[i];
1332                         if (optionNames.contains(propertyName)){
1333                                 options.put(propertyName, preferences.getString(propertyName).trim());
1334                         }
1335                 }               
1336                 return options;
1337         }
1338         
1339         /**
1340          * @see IJavaProject
1341          */
1342         public IPath getOutputLocation() throws JavaModelException {
1343
1344                 JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(fProject);
1345                 IPath outputLocation = perProjectInfo.outputLocation;
1346                 if (outputLocation != null) return outputLocation;
1347
1348                 // force to read classpath - will position output location as well
1349                 this.getRawClasspath();
1350                 outputLocation = perProjectInfo.outputLocation;
1351                 if (outputLocation == null) {
1352                         return defaultOutputLocation();
1353                 }
1354                 return outputLocation;
1355         }
1356
1357         /**
1358          * @return A handle to the package fragment root identified by the given path.
1359          * This method is handle-only and the element may or may not exist. Returns
1360          * <code>null</code> if unable to generate a handle from the path (for example,
1361          * an absolute path that has less than 1 segment. The path may be relative or
1362          * absolute.
1363          */
1364         public IPackageFragmentRoot getPackageFragmentRoot(IPath path) {
1365                 if (!path.isAbsolute()) {
1366                         path = getPath().append(path);
1367                 }
1368                 int segmentCount = path.segmentCount();
1369                 switch (segmentCount) {
1370                         case 0:
1371                                 return null;
1372                         case 1:
1373                                 // default root
1374                                 return getPackageFragmentRoot(getProject());
1375                         default:
1376                                 // a path ending with .jar/.zip is still ambiguous and could still resolve to a source/lib folder 
1377                                 // thus will try to guess based on existing resource
1378 //                              if (Util.isArchiveFileName(path.lastSegment())) {
1379 //                                      IResource resource = getProject().getWorkspace().getRoot().findMember(path); 
1380 //                                      if (resource != null && resource.getType() == IResource.FOLDER){
1381 //                                              return getPackageFragmentRoot(resource);
1382 //                                      }
1383 //                                      return getPackageFragmentRoot0(path);
1384 //                              } else {
1385                                         return getPackageFragmentRoot(getProject().getWorkspace().getRoot().getFolder(path));
1386 //                              }
1387                 }
1388         }
1389
1390         /**
1391          * The path is known to match a source/library folder entry.
1392          */
1393         public IPackageFragmentRoot getFolderPackageFragmentRoot(IPath path) {
1394                 if (path.segmentCount() == 1) { // default project root
1395                         return getPackageFragmentRoot(getProject());
1396                 }
1397                 return getPackageFragmentRoot(getProject().getWorkspace().getRoot().getFolder(path));
1398         }
1399         
1400         /**
1401          * @see IJavaProject
1402          */
1403         public IPackageFragmentRoot getPackageFragmentRoot(IResource resource) {
1404
1405                 switch (resource.getType()) {
1406                         case IResource.FILE:
1407 //                              if (Util.isArchiveFileName(resource.getName())) {
1408 //                                      return new JarPackageFragmentRoot(resource, this);
1409 //                              } else {
1410                                         return null;
1411 //                              }
1412                         case IResource.FOLDER:
1413                                 return new PackageFragmentRoot(resource, this, resource.getName());
1414                         case IResource.PROJECT:
1415                                 return new PackageFragmentRoot(resource, this, ""); //$NON-NLS-1$
1416                         default:
1417                                 return null;
1418                 }
1419         }
1420
1421         /**
1422          * @see IJavaProject
1423          */
1424 //      public IPackageFragmentRoot getPackageFragmentRoot(String jarPath) {
1425 //
1426 //              return getPackageFragmentRoot0(JavaProject.canonicalizedPath(new Path(jarPath)));
1427 //      }
1428 //      
1429 //      /**
1430 //       * no path canonicalization
1431 //       */
1432 //      public IPackageFragmentRoot getPackageFragmentRoot0(IPath jarPath) {
1433 //
1434 //              return new JarPackageFragmentRoot(jarPath, this);
1435 //      }
1436
1437         /**
1438          * @see IJavaProject
1439          */
1440         public IPackageFragmentRoot[] getPackageFragmentRoots()
1441                 throws JavaModelException {
1442
1443                 Object[] children;
1444                 int length;
1445                 IPackageFragmentRoot[] roots;
1446
1447                 System.arraycopy(
1448                         children = getChildren(), 
1449                         0, 
1450                         roots = new IPackageFragmentRoot[length = children.length], 
1451                         0, 
1452                         length);
1453                         
1454                 return roots;
1455         }
1456
1457         /**
1458          * @see IJavaProject
1459          * @deprecated
1460          */
1461         public IPackageFragmentRoot[] getPackageFragmentRoots(IClasspathEntry entry) {
1462                 return findPackageFragmentRoots(entry);
1463         }
1464
1465         /**
1466          * Returns the package fragment root prefixed by the given path, or
1467          * an empty collection if there are no such elements in the model.
1468          */
1469         protected IPackageFragmentRoot[] getPackageFragmentRoots(IPath path)
1470
1471                 throws JavaModelException {
1472                 IPackageFragmentRoot[] roots = getAllPackageFragmentRoots();
1473                 ArrayList matches = new ArrayList();
1474
1475                 for (int i = 0; i < roots.length; ++i) {
1476                         if (path.isPrefixOf(roots[i].getPath())) {
1477                                 matches.add(roots[i]);
1478                         }
1479                 }
1480                 IPackageFragmentRoot[] copy = new IPackageFragmentRoot[matches.size()];
1481                 matches.toArray(copy);
1482                 return copy;
1483         }
1484
1485         /**
1486          * @see IJavaProject
1487          */
1488         public IPackageFragment[] getPackageFragments() throws JavaModelException {
1489
1490                 IPackageFragmentRoot[] roots = getPackageFragmentRoots();
1491                 return getPackageFragmentsInRoots(roots);
1492         }
1493
1494         /**
1495          * Returns all the package fragments found in the specified
1496          * package fragment roots.
1497          */
1498         public IPackageFragment[] getPackageFragmentsInRoots(IPackageFragmentRoot[] roots) {
1499
1500                 ArrayList frags = new ArrayList();
1501                 for (int i = 0; i < roots.length; i++) {
1502                         IPackageFragmentRoot root = roots[i];
1503                         try {
1504                                 IJavaElement[] rootFragments = root.getChildren();
1505                                 for (int j = 0; j < rootFragments.length; j++) {
1506                                         frags.add(rootFragments[j]);
1507                                 }
1508                         } catch (JavaModelException e) {
1509                                 // do nothing
1510                         }
1511                 }
1512                 IPackageFragment[] fragments = new IPackageFragment[frags.size()];
1513                 frags.toArray(fragments);
1514                 return fragments;
1515         }
1516         
1517         /*
1518          * @see IJavaElement
1519          */
1520         public IPath getPath() {
1521                 return this.getProject().getFullPath();
1522         }
1523         
1524         /**
1525          * @see IJavaProject
1526          */
1527         public IProject getProject() {
1528
1529                 return fProject;
1530         }
1531         
1532         protected IProject getProject(String name) {
1533                 return PHPeclipsePlugin.getWorkspace().getRoot().getProject(name);
1534         }
1535         
1536         public List getReferencedProjects() {
1537                         List referencedProjects = new ArrayList();
1538
1539                         Iterator iterator = getLoadPathEntries().iterator();
1540                         while (iterator.hasNext()) {
1541                                 LoadPathEntry pathEntry = (LoadPathEntry) iterator.next();
1542                                 if (pathEntry.getType() == LoadPathEntry.TYPE_PROJECT)
1543                                         referencedProjects.add(pathEntry.getProject());
1544                         }
1545
1546                         return referencedProjects;
1547                 }
1548                 
1549         /**
1550          * Returns the project custom preference pool.
1551          * Project preferences may include custom encoding.
1552          */     
1553         public Preferences getPreferences(){
1554                 IProject project = getProject();
1555                 if (!JavaProject.hasJavaNature(project)) return null;
1556                 JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfo(project, true);
1557                 Preferences preferences =  perProjectInfo.preferences;
1558                 if (preferences != null) return preferences;
1559                 preferences = loadPreferences();
1560                 if (preferences == null) preferences = new Preferences();
1561                 perProjectInfo.preferences = preferences;
1562                 return preferences;
1563         }
1564
1565         /**
1566          * @see IJavaProject
1567          */
1568         public IClasspathEntry[] getRawClasspath() throws JavaModelException {
1569
1570                 JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(fProject);
1571                 IClasspathEntry[] classpath = perProjectInfo.classpath;
1572                 if (classpath != null) return classpath;
1573                 classpath = this.readClasspathFile(false/*don't create markers*/, true/*log problems*/);
1574                 
1575                 // extract out the output location
1576                 IPath outputLocation = null;
1577                 if (classpath != null && classpath.length > 0) {
1578                         IClasspathEntry entry = classpath[classpath.length - 1];
1579                         if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
1580                                 outputLocation = entry.getPath();
1581                                 IClasspathEntry[] copy = new IClasspathEntry[classpath.length - 1];
1582                                 System.arraycopy(classpath, 0, copy, 0, copy.length);
1583                                 classpath = copy;
1584                         }
1585                 }
1586                 if (classpath == null) {
1587                         return defaultClasspath();
1588                 }
1589                 /* Disable validate: classpath can contain CP variables and container that need to be resolved 
1590                 if (classpath != INVALID_CLASSPATH
1591                                 && !JavaConventions.validateClasspath(this, classpath, outputLocation).isOK()) {
1592                         classpath = INVALID_CLASSPATH;
1593                 }
1594                 */
1595                 perProjectInfo.classpath = classpath;
1596                 perProjectInfo.outputLocation = outputLocation;
1597                 return classpath;
1598         }
1599
1600         /**
1601          * @see IJavaProject#getRequiredProjectNames
1602          */
1603         public String[] getRequiredProjectNames() throws JavaModelException {
1604
1605                 return this.projectPrerequisites(getResolvedClasspath(true));
1606         }
1607
1608         /**
1609          * @see IJavaProject
1610          */
1611         public IClasspathEntry[] getResolvedClasspath(boolean ignoreUnresolvedEntry)
1612                 throws JavaModelException {
1613
1614                 return 
1615                         this.getResolvedClasspath(
1616                                 ignoreUnresolvedEntry, 
1617                                 false); // generateMarkerOnError
1618         }
1619
1620         /**
1621          * Internal variant which can create marker on project for invalid entries
1622          * and caches the resolved classpath on perProjectInfo
1623          */
1624         public IClasspathEntry[] getResolvedClasspath(
1625                 boolean ignoreUnresolvedEntry,
1626                 boolean generateMarkerOnError)
1627                 throws JavaModelException {
1628
1629                 JavaModelManager manager = JavaModelManager.getJavaModelManager();
1630                 JavaModelManager.PerProjectInfo perProjectInfo = manager.getPerProjectInfoCheckExistence(fProject);
1631                 
1632                 // reuse cache if not needing to refresh markers or checking bound variables
1633                 if (ignoreUnresolvedEntry && !generateMarkerOnError && perProjectInfo != null){
1634                         // resolved path is cached on its info
1635                         IClasspathEntry[] infoPath = perProjectInfo.lastResolvedClasspath;
1636                         if (infoPath != null) return infoPath;
1637                 }
1638                 Map reverseMap = perProjectInfo == null ? null : new HashMap(5);
1639                 IClasspathEntry[] resolvedPath = getResolvedClasspath(
1640                         getRawClasspath(), 
1641                         generateMarkerOnError ? getOutputLocation() : null, 
1642                         ignoreUnresolvedEntry, 
1643                         generateMarkerOnError,
1644                         reverseMap);
1645
1646                 if (perProjectInfo != null){
1647                         if (perProjectInfo.classpath == null // .classpath file could not be read
1648                                 && generateMarkerOnError 
1649                                 && JavaProject.hasJavaNature(fProject)) {
1650                                         this.createClasspathProblemMarker(new JavaModelStatus(
1651                                                 IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
1652                                                 Util.bind("classpath.cannotReadClasspathFile", this.getElementName()))); //$NON-NLS-1$
1653                                 }
1654
1655                         perProjectInfo.lastResolvedClasspath = resolvedPath;
1656                         perProjectInfo.resolvedPathToRawEntries = reverseMap;
1657                 }
1658                 return resolvedPath;
1659         }
1660         
1661         /**
1662          * Internal variant which can process any arbitrary classpath
1663          */
1664         public IClasspathEntry[] getResolvedClasspath(
1665                 IClasspathEntry[] classpathEntries,
1666                 IPath projectOutputLocation, // only set if needing full classpath validation (and markers)
1667                 boolean ignoreUnresolvedEntry, // if unresolved entries are met, should it trigger initializations
1668                 boolean generateMarkerOnError,
1669                 Map reverseMap) // can be null if not interested in reverse mapping
1670                 throws JavaModelException {
1671
1672                 IJavaModelStatus status;
1673                 if (generateMarkerOnError){
1674                         flushClasspathProblemMarkers(false, false);
1675                 }
1676
1677                 int length = classpathEntries.length;
1678                 ArrayList resolvedEntries = new ArrayList();
1679                 
1680                 for (int i = 0; i < length; i++) {
1681
1682                         IClasspathEntry rawEntry = classpathEntries[i];
1683                         IPath resolvedPath;
1684                         status = null;
1685                         
1686                         /* validation if needed */
1687 //                      if (generateMarkerOnError || !ignoreUnresolvedEntry) {
1688 //                              status = JavaConventions.validateClasspathEntry(this, rawEntry, false);
1689 //                              if (generateMarkerOnError && !status.isOK()) createClasspathProblemMarker(status);
1690 //                      }
1691
1692                         switch (rawEntry.getEntryKind()){
1693                                 
1694                                 case IClasspathEntry.CPE_VARIABLE :
1695                                 
1696                                         IClasspathEntry resolvedEntry = PHPCore.getResolvedClasspathEntry(rawEntry);
1697                                         if (resolvedEntry == null) {
1698                                                 if (!ignoreUnresolvedEntry) throw new JavaModelException(status);
1699                                         } else {
1700                                                 if (reverseMap != null && reverseMap.get(resolvedPath = resolvedEntry.getPath()) == null) reverseMap.put(resolvedPath , rawEntry);
1701                                                 resolvedEntries.add(resolvedEntry);
1702                                         }
1703                                         break; 
1704
1705 //                              case IClasspathEntry.CPE_CONTAINER :
1706 //                              
1707 //                                      IClasspathContainer container = PHPCore.getClasspathContainer(rawEntry.getPath(), this);
1708 //                                      if (container == null){
1709 //                                              if (!ignoreUnresolvedEntry) throw new JavaModelException(status);
1710 //                                              break;
1711 //                                      }
1712 //
1713 //                                      IClasspathEntry[] containerEntries = container.getClasspathEntries();
1714 //                                      if (containerEntries == null) break;
1715 //
1716 //                                      // container was bound
1717 //                                      for (int j = 0, containerLength = containerEntries.length; j < containerLength; j++){
1718 //                                              IClasspathEntry cEntry = containerEntries[j];
1719 //                                              
1720 //                                              if (generateMarkerOnError) {
1721 //                                                      IJavaModelStatus containerStatus = JavaConventions.validateClasspathEntry(this, cEntry, false);
1722 //                                                      if (!containerStatus.isOK()) createClasspathProblemMarker(containerStatus);
1723 //                                              }
1724 //                                              // if container is exported, then its nested entries must in turn be exported  (21749)
1725 //                                              if (rawEntry.isExported()){
1726 //                                                      cEntry = new ClasspathEntry(cEntry.getContentKind(),
1727 //                                                              cEntry.getEntryKind(), cEntry.getPath(),
1728 //                                                              cEntry.getExclusionPatterns(), cEntry.getSourceAttachmentPath(),
1729 //                                                              cEntry.getSourceAttachmentRootPath(), cEntry.getOutputLocation(), 
1730 //                                                              true); // duplicate container entry for tagging it as exported
1731 //                                              }
1732 //                                              if (reverseMap != null && reverseMap.get(resolvedPath = cEntry.getPath()) == null) reverseMap.put(resolvedPath, rawEntry);
1733 //                                              resolvedEntries.add(cEntry);
1734 //                                      }
1735 //                                      break;
1736                                                                                 
1737                                 default :
1738
1739                                         if (reverseMap != null && reverseMap.get(resolvedPath = rawEntry.getPath()) == null) reverseMap.put(resolvedPath, rawEntry);
1740                                         resolvedEntries.add(rawEntry);
1741                                 
1742                         }                                       
1743                 }
1744
1745                 IClasspathEntry[] resolvedPath = new IClasspathEntry[resolvedEntries.size()];
1746                 resolvedEntries.toArray(resolvedPath);
1747
1748 //              if (generateMarkerOnError && projectOutputLocation != null) {
1749 //                      status = JavaConventions.validateClasspath(this, resolvedPath, projectOutputLocation);
1750 //                      if (!status.isOK()) createClasspathProblemMarker(status);
1751 //              }
1752                 return resolvedPath;
1753         }
1754
1755         /*
1756          * @see IJavaElement
1757          */
1758         public IResource getResource() {
1759                 return this.getProject();
1760         }
1761
1762         /**
1763          * @see IJavaProject
1764          */
1765         public ISearchableNameEnvironment getSearchableNameEnvironment()
1766                 throws JavaModelException {
1767
1768                 JavaProjectElementInfo info = getJavaProjectElementInfo();
1769                 if (info.getSearchableEnvironment() == null) {
1770                         info.setSearchableEnvironment(new SearchableEnvironment(this));
1771                 }
1772                 return info.getSearchableEnvironment();
1773         }
1774
1775         /**
1776          * Retrieve a shared property on a project. If the property is not defined, answers null.
1777          * Note that it is orthogonal to IResource persistent properties, and client code has to decide
1778          * which form of storage to use appropriately. Shared properties produce real resource files which
1779          * can be shared through a VCM onto a server. Persistent properties are not shareable.
1780          *
1781          * @see JavaProject#setSharedProperty(String, String)
1782          */
1783         public String getSharedProperty(String key) throws CoreException {
1784
1785                 String property = null;
1786                 IFile rscFile = getProject().getFile(key);
1787                 if (rscFile.exists()) {
1788                         property = new String(Util.getResourceContentsAsByteArray(rscFile));
1789                 }
1790                 return property;
1791         }
1792
1793         /**
1794          * @see JavaElement
1795          */
1796 //      public SourceMapper getSourceMapper() {
1797 //
1798 //              return null;
1799 //      }
1800
1801         /**
1802          * @see IJavaElement
1803          */
1804         public IResource getUnderlyingResource() throws JavaModelException {
1805                 if (!exists()) throw newNotPresentException();
1806                 return getProject();
1807         }
1808
1809         /**
1810          * @see IJavaProject
1811          */
1812         public boolean hasBuildState() {
1813
1814                 return JavaModelManager.getJavaModelManager().getLastBuiltState(this.getProject(), null) != null;
1815         }
1816
1817         /**
1818          * @see IJavaProject
1819          */
1820         public boolean hasClasspathCycle(IClasspathEntry[] preferredClasspath) {
1821                 HashSet cycleParticipants = new HashSet();
1822                 updateCycleParticipants(preferredClasspath, new ArrayList(2), cycleParticipants, ResourcesPlugin.getWorkspace().getRoot(), new HashSet(2));
1823                 return !cycleParticipants.isEmpty();
1824         }
1825         
1826         public boolean hasCycleMarker(){
1827                 return this.getCycleMarker() != null;
1828         }
1829
1830         public int hashCode() {
1831                 return fProject.hashCode();
1832         }
1833
1834         /**
1835          * Returns true if the given project is accessible and it has
1836          * a java nature, otherwise false.
1837          */
1838         public static boolean hasJavaNature(IProject project) { 
1839                 try {
1840                         return project.hasNature(PHPeclipsePlugin.PHP_NATURE_ID);
1841                 } catch (CoreException e) {
1842                         // project does not exist or is not open
1843                 }
1844                 return false;
1845         }
1846         
1847         /**
1848          * Answers true if the project potentially contains any source. A project which has no source is immutable.
1849          */
1850         public boolean hasSource() {
1851
1852                 // look if any source folder on the classpath
1853                 // no need for resolved path given source folder cannot be abstracted
1854                 IClasspathEntry[] entries;
1855                 try {
1856                         entries = this.getRawClasspath();
1857                 } catch (JavaModelException e) {
1858                         return true; // unsure
1859                 }
1860                 for (int i = 0, max = entries.length; i < max; i++) {
1861                         if (entries[i].getEntryKind() == IClasspathEntry.CPE_SOURCE) {
1862                                 return true;
1863                         }
1864                 }
1865                 return false;
1866         }
1867
1868         /**
1869          * Compare current classpath with given one to see if any different.
1870          * Note that the argument classpath contains its binary output.
1871          */
1872         public boolean isClasspathEqualsTo(IClasspathEntry[] newClasspath, IPath newOutputLocation, IClasspathEntry[] otherClasspathWithOutput)
1873                 throws JavaModelException {
1874
1875                 if (otherClasspathWithOutput != null && otherClasspathWithOutput.length > 0) {
1876
1877                         int length = otherClasspathWithOutput.length;
1878                         if (length == newClasspath.length + 1) {
1879                                 // output is amongst file entries (last one)
1880
1881                                 // compare classpath entries
1882                                 for (int i = 0; i < length - 1; i++) {
1883                                         if (!otherClasspathWithOutput[i].equals(newClasspath[i]))
1884                                                 return false;
1885                                 }
1886                                 // compare binary outputs
1887                                 IClasspathEntry output = otherClasspathWithOutput[length - 1];
1888                                 if (output.getContentKind() == ClasspathEntry.K_OUTPUT
1889                                                 && output.getPath().equals(newOutputLocation))
1890                                         return true;
1891                         }
1892                 }
1893                 return false;
1894         }
1895         
1896
1897         
1898         /*
1899          * @see IJavaProject
1900          */
1901         public boolean isOnClasspath(IJavaElement element) {
1902                 IPath path = element.getPath();
1903                 switch (element.getElementType()) {
1904                         case IJavaElement.PACKAGE_FRAGMENT_ROOT:
1905                                 if (!((IPackageFragmentRoot)element).isArchive()) {
1906                                         // ensure that folders are only excluded if all of their children are excluded
1907                                         path = path.append("*"); //$NON-NLS-1$
1908                                 }
1909                                 break;
1910                         case IJavaElement.PACKAGE_FRAGMENT:
1911                                 if (!((IPackageFragmentRoot)element.getParent()).isArchive()) {
1912                                         // ensure that folders are only excluded if all of their children are excluded
1913                                         path = path.append("*"); //$NON-NLS-1$
1914                                 }
1915                                 break;
1916                 }
1917                 return this.isOnClasspath(path);
1918         }
1919         private boolean isOnClasspath(IPath path) {
1920                 IClasspathEntry[] classpath;
1921                 try {
1922                         classpath = this.getResolvedClasspath(true/*ignore unresolved variable*/);
1923                 } catch(JavaModelException e){
1924                         return false; // not a Java project
1925                 }
1926                 for (int i = 0; i < classpath.length; i++) {
1927                         IClasspathEntry entry = classpath[i];
1928                         if (entry.getPath().isPrefixOf(path) 
1929                                         && !Util.isExcluded(path, ((ClasspathEntry)entry).fullExclusionPatternChars())) {
1930                                 return true;
1931                         }
1932                 }
1933                 return false;
1934         }
1935         /*
1936          * @see IJavaProject
1937          */
1938         public boolean isOnClasspath(IResource resource) {
1939                 IPath path = resource.getFullPath();
1940                 
1941                 // ensure that folders are only excluded if all of their children are excluded
1942                 if (resource.getType() == IResource.FOLDER) {
1943                         path = path.append("*"); //$NON-NLS-1$
1944                 }
1945                 
1946                 return this.isOnClasspath(path);
1947         }
1948
1949
1950         /*
1951          * load preferences from a shareable format (VCM-wise)
1952          */
1953          public Preferences loadPreferences() {
1954                 
1955                 Preferences preferences = new Preferences();
1956                 
1957 //              File prefFile = getProject().getLocation().append(PREF_FILENAME).toFile();
1958                 IPath projectMetaLocation = getProject().getPluginWorkingLocation(PHPCore.getPlugin().getDescriptor());
1959                 if (projectMetaLocation != null) {
1960                         File prefFile = projectMetaLocation.append(PREF_FILENAME).toFile();
1961                         if (prefFile.exists()) { // load preferences from file
1962                                 InputStream in = null;
1963                                 try {
1964                                         in = new BufferedInputStream(new FileInputStream(prefFile));
1965                                         preferences.load(in);
1966                                         return preferences;
1967                                 } catch (IOException e) { // problems loading preference store - quietly ignore
1968                                 } finally {
1969                                         if (in != null) {
1970                                                 try {
1971                                                         in.close();
1972                                                 } catch (IOException e) { // ignore problems with close
1973                                                 }
1974                                         }
1975                                 }
1976                         }
1977                 }
1978                 return null;
1979          }
1980          
1981         /**
1982          * @see IJavaProject#newEvaluationContext
1983          */
1984 //      public IEvaluationContext newEvaluationContext() {
1985 //
1986 //              return new EvaluationContextWrapper(new EvaluationContext(), this);
1987 //      }
1988
1989         /**
1990          * @see IJavaProject
1991          */
1992 //      public ITypeHierarchy newTypeHierarchy(
1993 //              IRegion region,
1994 //              IProgressMonitor monitor)
1995 //              throws JavaModelException {
1996 //
1997 //              if (region == null) {
1998 //                      throw new IllegalArgumentException(Util.bind("hierarchy.nullRegion"));//$NON-NLS-1$
1999 //              }
2000 //              CreateTypeHierarchyOperation op =
2001 //                      new CreateTypeHierarchyOperation(null, region, this, true);
2002 //              runOperation(op, monitor);
2003 //              return op.getResult();
2004 //      }
2005
2006         /**
2007          * @see IJavaProject
2008          */
2009 //      public ITypeHierarchy newTypeHierarchy(
2010 //              IType type,
2011 //              IRegion region,
2012 //              IProgressMonitor monitor)
2013 //              throws JavaModelException {
2014 //
2015 //              if (type == null) {
2016 //                      throw new IllegalArgumentException(Util.bind("hierarchy.nullFocusType"));//$NON-NLS-1$
2017 //              }
2018 //              if (region == null) {
2019 //                      throw new IllegalArgumentException(Util.bind("hierarchy.nullRegion"));//$NON-NLS-1$
2020 //              }
2021 //              CreateTypeHierarchyOperation op =
2022 //                      new CreateTypeHierarchyOperation(type, region, this, true);
2023 //              runOperation(op, monitor);
2024 //              return op.getResult();
2025 //      }
2026
2027         /**
2028          * Open project if resource isn't closed
2029          */
2030         protected void openWhenClosed(IProgressMonitor pm) throws JavaModelException {
2031
2032                 if (!this.fProject.isOpen()) {
2033                         throw newNotPresentException();
2034                 } else {
2035                         super.openWhenClosed(pm);
2036                 }
2037         }
2038
2039         public String[] projectPrerequisites(IClasspathEntry[] entries)
2040                 throws JavaModelException {
2041                         
2042                 ArrayList prerequisites = new ArrayList();
2043                 // need resolution
2044                 entries = getResolvedClasspath(entries, null, true, false, null/*no reverse map*/);
2045                 for (int i = 0, length = entries.length; i < length; i++) {
2046                         IClasspathEntry entry = entries[i];
2047                         if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
2048                                 prerequisites.add(entry.getPath().lastSegment());
2049                         }
2050                 }
2051                 int size = prerequisites.size();
2052                 if (size == 0) {
2053                         return NO_PREREQUISITES;
2054                 } else {
2055                         String[] result = new String[size];
2056                         prerequisites.toArray(result);
2057                         return result;
2058                 }
2059         }
2060
2061
2062         /**
2063          * Reads the .classpath file from disk and returns the list of entries it contains (including output location entry)
2064          * Returns null if .classfile is not present.
2065          * Returns INVALID_CLASSPATH if it has a format problem.
2066          */
2067         protected IClasspathEntry[] readClasspathFile(boolean createMarker, boolean logProblems) {
2068
2069                 try {
2070                         String xmlClasspath = getSharedProperty(CLASSPATH_FILENAME);
2071                         if (xmlClasspath == null) return null;
2072                         return decodeClasspath(xmlClasspath, createMarker, logProblems);
2073                 } catch(CoreException e) {
2074                         // file does not exist (or not accessible)
2075                         if (createMarker && this.getProject().isAccessible()) {
2076                                         this.createClasspathProblemMarker(new JavaModelStatus(
2077                                                 IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
2078                                                 Util.bind("classpath.cannotReadClasspathFile", this.getElementName()))); //$NON-NLS-1$
2079                         }
2080                         if (logProblems) {
2081                                 Util.log(e, 
2082                                         "Exception while retrieving "+ this.getPath() //$NON-NLS-1$
2083                                         +"/.classpath, will revert to default classpath"); //$NON-NLS-1$
2084                         }
2085                 }
2086                 return null;
2087         }
2088
2089         /**
2090          * Removes the given builder from the build spec for the given project.
2091          */
2092         protected void removeFromBuildSpec(String builderID) throws CoreException {
2093
2094                 IProjectDescription description = getProject().getDescription();
2095                 ICommand[] commands = description.getBuildSpec();
2096                 for (int i = 0; i < commands.length; ++i) {
2097                         if (commands[i].getBuilderName().equals(builderID)) {
2098                                 ICommand[] newCommands = new ICommand[commands.length - 1];
2099                                 System.arraycopy(commands, 0, newCommands, 0, i);
2100                                 System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
2101                                 description.setBuildSpec(newCommands);
2102                                 getProject().setDescription(description, null);
2103                                 return;
2104                         }
2105                 }
2106         }
2107
2108
2109         /**
2110          * @see JavaElement#rootedAt(IJavaProject)
2111          */
2112         public IJavaElement rootedAt(IJavaProject project) {
2113                 return project;
2114         
2115         }
2116         
2117         /**
2118          * Answers an ID which is used to distinguish project/entries during package
2119          * fragment root computations
2120          */
2121         public String rootID(){
2122                 return "[PRJ]"+this.getProject().getFullPath(); //$NON-NLS-1$
2123         }
2124         
2125         /**
2126          * Saves the classpath in a shareable format (VCM-wise) only when necessary, that is, if  it is semantically different
2127          * from the existing one in file. Will never write an identical one.
2128          * 
2129          * @return Return whether the .classpath file was modified.
2130          */
2131         public boolean saveClasspath(IClasspathEntry[] newClasspath, IPath newOutputLocation) throws JavaModelException {
2132
2133                 if (!getProject().exists()) return false;
2134
2135                 IClasspathEntry[] fileEntries = readClasspathFile(false /*don't create markers*/, false/*don't log problems*/);
2136                 if (fileEntries != null && isClasspathEqualsTo(newClasspath, newOutputLocation, fileEntries)) {
2137                         // no need to save it, it is the same
2138                         return false;
2139                 }
2140
2141                 // actual file saving
2142                 try {
2143                         setSharedProperty(CLASSPATH_FILENAME, encodeClasspath(newClasspath, newOutputLocation, true));
2144                         return true;
2145                 } catch (CoreException e) {
2146                         throw new JavaModelException(e);
2147                 }
2148         }
2149
2150         /**
2151          * Save project custom preferences to shareable file (.jprefs)
2152          */
2153         private void savePreferences(Preferences preferences) {
2154                 
2155                 IProject project = getProject();
2156                 if (!JavaProject.hasJavaNature(project)) return; // ignore
2157                 
2158                 if (preferences == null || (!preferences.needsSaving() && preferences.propertyNames().length != 0)) {
2159                         // nothing to save
2160                         return;
2161                 }
2162         
2163                 // preferences need to be saved
2164                 // the preferences file is located in the plug-in's state area
2165                 // at a well-known name (.jprefs)
2166 //              File prefFile = getProject().getLocation().append(PREF_FILENAME).toFile();
2167                 File prefFile = project.getPluginWorkingLocation(PHPCore.getPlugin().getDescriptor()).append(PREF_FILENAME).toFile();
2168                 if (preferences.propertyNames().length == 0) {
2169                         // there are no preference settings
2170                         // rather than write an empty file, just delete any existing file
2171                         if (prefFile.exists()) {
2172                                 prefFile.delete(); // don't worry if delete unsuccessful
2173                         }
2174                         return;
2175                 }
2176                 
2177                 // write file, overwriting an existing one
2178                 OutputStream out = null;
2179                 try {
2180                         // do it as carefully as we know how so that we don't lose/mangle
2181                         // the setting in times of stress
2182                         out = new BufferedOutputStream(new FileOutputStream(prefFile));
2183                         preferences.store(out, null);
2184                 } catch (IOException e) { // problems saving preference store - quietly ignore
2185                 } finally {
2186                         if (out != null) {
2187                                 try {
2188                                         out.close();
2189                                 } catch (IOException e) { // ignore problems with close
2190                                 }
2191                         }
2192                 }
2193         }
2194
2195         /**
2196          * Update the Java command in the build spec (replace existing one if present,
2197          * add one first if none).
2198          */
2199         private void setJavaCommand(
2200                 IProjectDescription description,
2201                 ICommand newCommand)
2202                 throws CoreException {
2203
2204                 ICommand[] oldCommands = description.getBuildSpec();
2205                 ICommand oldJavaCommand = getJavaCommand(description);
2206                 ICommand[] newCommands;
2207
2208                 if (oldJavaCommand == null) {
2209                         // Add a Java build spec before other builders (1FWJK7I)
2210                         newCommands = new ICommand[oldCommands.length + 1];
2211                         System.arraycopy(oldCommands, 0, newCommands, 1, oldCommands.length);
2212                         newCommands[0] = newCommand;
2213                 } else {
2214                         for (int i = 0, max = oldCommands.length; i < max; i++) {
2215                                 if (oldCommands[i] == oldJavaCommand) {
2216                                         oldCommands[i] = newCommand;
2217                                         break;
2218                                 }
2219                         }
2220                         newCommands = oldCommands;
2221                 }
2222
2223                 // Commit the spec change into the project
2224                 description.setBuildSpec(newCommands);
2225                 getProject().setDescription(description, null);
2226         }
2227
2228         /**
2229          * @see org.eclipse.jdt.core.IJavaProject#setOptions(Map)
2230          */
2231         public void setOptions(Map newOptions) {
2232
2233                 Preferences preferences;
2234                 setPreferences(preferences = new Preferences()); // always reset (26255)
2235                 if (newOptions != null){
2236                         Iterator keys = newOptions.keySet().iterator();
2237                         while (keys.hasNext()){
2238                                 String key = (String)keys.next();
2239                                 if (!JavaModelManager.OptionNames.contains(key)) continue; // unrecognized option
2240                                 // no filtering for encoding (custom encoding for project is allowed)
2241                                 String value = (String)newOptions.get(key);
2242                                 preferences.setDefault(key, CUSTOM_DEFAULT_OPTION_VALUE); // empty string isn't the default (26251)
2243                                 preferences.setValue(key, value);
2244                         }
2245                 }
2246                 
2247                 // persist options
2248                 savePreferences(preferences);   
2249         }
2250
2251         /**
2252          * @see IJavaProject
2253          */
2254         public void setOutputLocation(IPath path, IProgressMonitor monitor)
2255                 throws JavaModelException {
2256
2257                 if (path == null) {
2258                         throw new IllegalArgumentException(Util.bind("path.nullpath")); //$NON-NLS-1$
2259                 }
2260                 if (path.equals(getOutputLocation())) {
2261                         return;
2262                 }
2263                 this.setRawClasspath(SetClasspathOperation.ReuseClasspath, path, monitor);
2264         }
2265
2266         /*
2267          * Set cached preferences, no preference file is saved, only info is updated
2268          */
2269         public void setPreferences(Preferences preferences) {
2270                 IProject project = getProject();
2271                 if (!JavaProject.hasJavaNature(project)) return; // ignore
2272                 JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfo(project, true);
2273                 perProjectInfo.preferences = preferences;
2274         }
2275
2276         /**
2277          * Sets the underlying kernel project of this Java project,
2278          * and fills in its parent and name.
2279          * Called by IProject.getNature().
2280          *
2281          * @see IProjectNature#setProject
2282          */
2283         public void setProject(IProject project) {
2284
2285                 fProject = project;
2286                 fParent = JavaModelManager.getJavaModelManager().getJavaModel();
2287                 fName = project.getName();
2288         }
2289
2290         /**
2291          * @see IJavaProject
2292          */
2293         public void setRawClasspath(
2294                 IClasspathEntry[] entries,
2295                 IPath outputLocation,
2296                 IProgressMonitor monitor)
2297                 throws JavaModelException {
2298
2299                 setRawClasspath(
2300                         entries, 
2301                         outputLocation, 
2302                         monitor, 
2303                         true, // canChangeResource (as per API contract)
2304                         getResolvedClasspath(true), // ignoreUnresolvedVariable
2305                         true, // needValidation
2306                         true); // need to save
2307         }
2308
2309         public void setRawClasspath(
2310                 IClasspathEntry[] newEntries,
2311                 IPath newOutputLocation,
2312                 IProgressMonitor monitor,
2313                 boolean canChangeResource,
2314                 IClasspathEntry[] oldResolvedPath,
2315                 boolean needValidation,
2316                 boolean needSave)
2317                 throws JavaModelException {
2318
2319                 JavaModelManager manager =
2320                         (JavaModelManager) JavaModelManager.getJavaModelManager();
2321                 try {
2322                         IClasspathEntry[] newRawPath = newEntries;
2323                         if (newRawPath == null) { //are we already with the default classpath
2324                                 newRawPath = defaultClasspath();
2325                         }
2326                         SetClasspathOperation op =
2327                                 new SetClasspathOperation(
2328                                         this, 
2329                                         oldResolvedPath, 
2330                                         newRawPath, 
2331                                         newOutputLocation,
2332                                         canChangeResource, 
2333                                         needValidation,
2334                                         needSave);
2335                         runOperation(op, monitor);
2336                         
2337                 } catch (JavaModelException e) {
2338                         manager.flush();
2339                         throw e;
2340                 }
2341         }
2342
2343         /**
2344          * @see IJavaProject
2345          */
2346         public void setRawClasspath(
2347                 IClasspathEntry[] entries,
2348                 IProgressMonitor monitor)
2349                 throws JavaModelException {
2350
2351                 setRawClasspath(
2352                         entries, 
2353                         SetClasspathOperation.ReuseOutputLocation, 
2354                         monitor, 
2355                         true, // canChangeResource (as per API contract)
2356                         getResolvedClasspath(true), // ignoreUnresolvedVariable
2357                         true, // needValidation
2358                         true); // need to save
2359         }
2360
2361         /**
2362          * NOTE: <code>null</code> specifies default classpath, and an empty
2363          * array specifies an empty classpath.
2364          *
2365          * @exception NotPresentException if this project does not exist.
2366          */
2367         protected void setRawClasspath0(IClasspathEntry[] rawEntries)
2368                 throws JavaModelException {
2369
2370                 JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(fProject);
2371         
2372                 synchronized (info) {
2373                         if (rawEntries != null) {
2374                                 info.classpath = rawEntries;
2375                         }
2376                         
2377                         // clear cache of resolved classpath
2378                         info.lastResolvedClasspath = null;
2379                         info.resolvedPathToRawEntries = null;
2380                 }
2381         }
2382
2383         /**
2384          * Record a shared persistent property onto a project.
2385          * Note that it is orthogonal to IResource persistent properties, and client code has to decide
2386          * which form of storage to use appropriately. Shared properties produce real resource files which
2387          * can be shared through a VCM onto a server. Persistent properties are not shareable.
2388          * 
2389          * shared properties end up in resource files, and thus cannot be modified during
2390          * delta notifications (a CoreException would then be thrown).
2391          * 
2392          * @see JavaProject#getSharedProperty(String key)
2393          */
2394         public void setSharedProperty(String key, String value) throws CoreException {
2395
2396                 IFile rscFile = getProject().getFile(key);
2397                 InputStream inputStream = new ByteArrayInputStream(value.getBytes());
2398                 // update the resource content
2399                 if (rscFile.exists()) {
2400                         if (rscFile.isReadOnly()) {
2401                                 // provide opportunity to checkout read-only .classpath file (23984)
2402                                 ResourcesPlugin.getWorkspace().validateEdit(new IFile[]{rscFile}, null);
2403                         }
2404                         rscFile.setContents(inputStream, IResource.FORCE, null);
2405                 } else {
2406                         rscFile.create(inputStream, IResource.FORCE, null);
2407                 }
2408         }
2409
2410         /**
2411          * Update cycle markers for all java projects
2412          */
2413         public static void updateAllCycleMarkers() throws JavaModelException {
2414
2415                 //long start = System.currentTimeMillis();
2416
2417                 JavaModelManager manager = JavaModelManager.getJavaModelManager();
2418                 IJavaProject[] projects = manager.getJavaModel().getJavaProjects();
2419                 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
2420
2421                 HashSet cycleParticipants = new HashSet();
2422                 HashSet traversed = new HashSet();
2423                 int length = projects.length;
2424                 
2425                 // compute cycle participants
2426                 ArrayList prereqChain = new ArrayList();
2427                 for (int i = 0; i < length; i++){
2428                         JavaProject project = (JavaProject)projects[i];
2429                         if (!traversed.contains(project.getPath())){
2430                                 prereqChain.clear();
2431                                 project.updateCycleParticipants(null, prereqChain, cycleParticipants, workspaceRoot, traversed);
2432                         }
2433                 }
2434                 //System.out.println("updateAllCycleMarkers: " + (System.currentTimeMillis() - start) + " ms");
2435
2436                 for (int i = 0; i < length; i++){
2437                         JavaProject project = (JavaProject)projects[i];
2438                         
2439                         if (cycleParticipants.contains(project.getPath())){
2440                                 IMarker cycleMarker = project.getCycleMarker();
2441                                 String circularCPOption = project.getOption(PHPCore.CORE_CIRCULAR_CLASSPATH, true);
2442                                 int circularCPSeverity = PHPCore.ERROR.equals(circularCPOption) ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING;
2443                                 if (cycleMarker != null) {
2444                                         // update existing cycle marker if needed
2445                                         try {
2446                                                 int existingSeverity = ((Integer)cycleMarker.getAttribute(IMarker.SEVERITY)).intValue();
2447                                                 if (existingSeverity != circularCPSeverity) {
2448                                                         cycleMarker.setAttribute(IMarker.SEVERITY, circularCPSeverity);
2449                                                 }
2450                                         } catch (CoreException e) {
2451                                                 throw new JavaModelException(e);
2452                                         }
2453                                 } else {
2454                                         // create new marker
2455                                         project.createClasspathProblemMarker(
2456                                                 new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE, project)); 
2457                                 }
2458                         } else {
2459                                 project.flushClasspathProblemMarkers(true, false);
2460                         }                       
2461                 }
2462         }
2463
2464         /**
2465          * If a cycle is detected, then cycleParticipants contains all the paths of projects involved in this cycle (directly and indirectly),
2466          * no cycle if the set is empty (and started empty)
2467          */
2468         public void updateCycleParticipants(
2469                         IClasspathEntry[] preferredClasspath, 
2470                         ArrayList prereqChain, 
2471                         HashSet cycleParticipants, 
2472                         IWorkspaceRoot workspaceRoot,
2473                         HashSet traversed){
2474
2475                 IPath path = this.getPath();
2476                 prereqChain.add(path);
2477                 traversed.add(path);
2478                 try {
2479                         IClasspathEntry[] classpath = preferredClasspath == null ? getResolvedClasspath(true) : preferredClasspath;
2480                         for (int i = 0, length = classpath.length; i < length; i++) {
2481                                 IClasspathEntry entry = classpath[i];
2482                                 
2483                                 if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT){
2484                                         IPath prereqProjectPath = entry.getPath();
2485                                         int index = cycleParticipants.contains(prereqProjectPath) ? 0 : prereqChain.indexOf(prereqProjectPath);
2486                                         if (index >= 0) { // refer to cycle, or in cycle itself
2487                                                 for (int size = prereqChain.size(); index < size; index++) {
2488                                                         cycleParticipants.add(prereqChain.get(index)); 
2489                                                 }
2490                                         } else {
2491                                                 if (!traversed.contains(prereqProjectPath)) {
2492                                                         IResource member = workspaceRoot.findMember(prereqProjectPath);
2493                                                         if (member != null && member.getType() == IResource.PROJECT){
2494                                                                 JavaProject project = (JavaProject)PHPCore.create((IProject)member);
2495                                                                 project.updateCycleParticipants(null, prereqChain, cycleParticipants, workspaceRoot, traversed);
2496                                                         }
2497                                                 }
2498                                         }
2499                                 }
2500                         }
2501                 } catch(JavaModelException e){
2502                 }
2503                 prereqChain.remove(path);
2504         }
2505         /**
2506          * Reset the collection of package fragment roots (local ones) - only if opened.
2507          * Need to check *all* package fragment roots in order to reset NameLookup
2508          */
2509         public void updatePackageFragmentRoots(){
2510                 
2511                         if (this.isOpen()) {
2512                                 try {
2513                                         JavaProjectElementInfo info = getJavaProjectElementInfo();
2514
2515                                         IClasspathEntry[] classpath = getResolvedClasspath(true);
2516 //                                      NameLookup lookup = info.getNameLookup();
2517 //                                      if (lookup != null){
2518 //                                              IPackageFragmentRoot[] oldRoots = lookup.fPackageFragmentRoots;
2519 //                                              IPackageFragmentRoot[] newRoots = computePackageFragmentRoots(classpath, true);
2520 //                                              checkIdentical: { // compare all pkg fragment root lists
2521 //                                                      if (oldRoots.length == newRoots.length){
2522 //                                                              for (int i = 0, length = oldRoots.length; i < length; i++){
2523 //                                                                      if (!oldRoots[i].equals(newRoots[i])){
2524 //                                                                              break checkIdentical;
2525 //                                                                      }
2526 //                                                              }
2527 //                                                              return; // no need to update
2528 //                                                      }       
2529 //                                              }
2530 //                                              info.setNameLookup(null); // discard name lookup (hold onto roots)
2531 //                                      }                               
2532                                         info.setNonJavaResources(null);
2533                                         info.setChildren(
2534                                                 computePackageFragmentRoots(classpath, false));         
2535
2536                                 } catch(JavaModelException e){
2537                                         try {
2538                                                 close(); // could not do better
2539                                         } catch(JavaModelException ex){
2540                                         }
2541                                 }
2542                         }
2543         }
2544
2545                 public void removeLoadPathEntry(IProject anotherPHPProject) {
2546                         Iterator entries = getLoadPathEntries().iterator();
2547                         while (entries.hasNext()) {
2548                                 LoadPathEntry entry = (LoadPathEntry) entries.next();
2549                                 if (entry.getType() == LoadPathEntry.TYPE_PROJECT && entry.getProject().getName().equals(anotherPHPProject.getName())) {
2550                                         getLoadPathEntries().remove(entry);
2551                                         fScratched = true;
2552                                         break;
2553                                 }
2554                         }
2555                 }
2556
2557                 public void save() throws CoreException {
2558                         if (fScratched) {
2559                                 InputStream xmlPath = new ByteArrayInputStream(getLoadPathXML().getBytes());
2560                                 IFile loadPathsFile = getLoadPathEntriesFile();
2561                                 if (!loadPathsFile.exists())
2562                                         loadPathsFile.create(xmlPath, true, null);
2563                                 else
2564                                         loadPathsFile.setContents(xmlPath, true, false, null);
2565
2566                                 fScratched = false;
2567                         }
2568                 }
2569
2570 }