fc56c48ff2ba4afced98d93213cfc0a3d1a54e29
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / ClasspathEntry.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.util.HashMap;
14 import java.util.HashSet;
15
16 import net.sourceforge.phpdt.core.IClasspathEntry;
17 import net.sourceforge.phpdt.core.IJavaModelStatus;
18 import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
19 import net.sourceforge.phpdt.core.IJavaProject;
20 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
21 import net.sourceforge.phpdt.core.JavaCore;
22 import net.sourceforge.phpdt.core.JavaModelException;
23 import net.sourceforge.phpdt.core.compiler.CharOperation;
24 import net.sourceforge.phpdt.internal.core.util.Util;
25 import net.sourceforge.phpdt.internal.corext.Assert;
26 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
27
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IWorkspaceRoot;
30 import org.eclipse.core.resources.ResourcesPlugin;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IPath;
33 import org.eclipse.core.runtime.Path;
34 import org.w3c.dom.Document;
35 import org.w3c.dom.Element;
36
37 /**
38  * @see IClasspathEntry
39  */
40 public class ClasspathEntry implements IClasspathEntry {
41
42         /**
43          * Describes the kind of classpath entry - one of CPE_PROJECT, CPE_LIBRARY,
44          * CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
45          */
46         public int entryKind;
47
48         /**
49          * Describes the kind of package fragment roots found on this classpath
50          * entry - either K_BINARY or K_SOURCE or K_OUTPUT.
51          */
52         public int contentKind;
53
54         /**
55          * The meaning of the path of a classpath entry depends on its entry kind:
56          * <ul>
57          * <li>Source code in the current project (<code>CPE_SOURCE</code>) -
58          * The path associated with this entry is the absolute path to the root
59          * folder. </li>
60          * <li>A binary library in the current project (<code>CPE_LIBRARY</code>) -
61          * the path associated with this entry is the absolute path to the JAR (or
62          * root folder), and in case it refers to an external JAR, then there is no
63          * associated resource in the workbench.
64          * <li>A required project (<code>CPE_PROJECT</code>) - the path of the
65          * entry denotes the path to the corresponding project resource.</li>
66          * <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment
67          * of the path is the name of a classpath variable. If this classpath
68          * variable is bound to the path <it>P</it>, the path of the corresponding
69          * classpath entry is computed by appending to <it>P</it> the segments of
70          * the returned path without the variable.</li>
71          * <li> A container entry (<code>CPE_CONTAINER</code>) - the first
72          * segment of the path is denoting the unique container identifier (for
73          * which a <code>ClasspathContainerInitializer</code> could be
74          * registered), and the remaining segments are used as additional hints for
75          * resolving the container entry to an actual
76          * <code>IClasspathContainer</code>.</li>
77          */
78         public IPath path;
79
80         /**
81          * Patterns allowing to include/exclude portions of the resource tree
82          * denoted by this entry path.
83          */
84         public IPath[] inclusionPatterns;
85
86         private char[][] fullCharInclusionPatterns;
87
88         public IPath[] exclusionPatterns;
89
90         private char[][] fullCharExclusionPatterns;
91
92         private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
93
94         private String rootID;
95
96         /*
97          * Default inclusion pattern set
98          */
99         public final static IPath[] INCLUDE_ALL = {};
100
101         /*
102          * Default exclusion pattern set
103          */
104         public final static IPath[] EXCLUDE_NONE = {};
105
106         /**
107          * Default exclusion pattern set
108          */
109         public final static IPath[] NO_EXCLUSION_PATTERNS = {};
110
111         /**
112          * Describes the path to the source archive associated with this classpath
113          * entry, or <code>null</code> if this classpath entry has no source
114          * attachment.
115          * <p>
116          * Only library and variable classpath entries may have source attachments.
117          * For library classpath entries, the result path (if present) locates a
118          * source archive. For variable classpath entries, the result path (if
119          * present) has an analogous form and meaning as the variable path, namely
120          * the first segment is the name of a classpath variable.
121          */
122         public IPath sourceAttachmentPath;
123
124         /**
125          * Describes the path within the source archive where package fragments are
126          * located. An empty path indicates that packages are located at the root of
127          * the source archive. Returns a non-<code>null</code> value if and only
128          * if <code>getSourceAttachmentPath</code> returns a non-<code>null</code>
129          * value.
130          */
131         public IPath sourceAttachmentRootPath;
132
133         /**
134          * Specific output location (for this source entry)
135          */
136         public IPath specificOutputLocation;
137
138         /**
139          * A constant indicating an output location.
140          */
141         public static final int K_OUTPUT = 10;
142
143         /**
144          * The export flag
145          */
146         public boolean isExported;
147
148         /**
149          * Creates a class path entry of the specified kind with the given path.
150          */
151         public ClasspathEntry(int contentKind, int entryKind, IPath path,
152                         IPath[] inclusionPatterns, IPath[] exclusionPatterns,
153                         IPath sourceAttachmentPath, IPath sourceAttachmentRootPath,
154                         IPath specificOutputLocation, boolean isExported) {
155
156                 this.contentKind = contentKind;
157                 this.entryKind = entryKind;
158                 this.path = path;
159                 this.inclusionPatterns = inclusionPatterns;
160                 if (inclusionPatterns != INCLUDE_ALL && inclusionPatterns.length > 0) {
161                         this.fullCharInclusionPatterns = UNINIT_PATTERNS;
162                 } else {
163                         this.fullCharInclusionPatterns = null; // empty inclusion pattern
164                                                                                                         // means everything is
165                                                                                                         // included
166                 }
167                 this.exclusionPatterns = exclusionPatterns;
168                 if (exclusionPatterns.length > 0) {
169                         this.fullCharExclusionPatterns = UNINIT_PATTERNS;
170                 }
171                 this.sourceAttachmentPath = sourceAttachmentPath;
172                 this.sourceAttachmentRootPath = sourceAttachmentRootPath;
173                 this.specificOutputLocation = specificOutputLocation;
174                 this.isExported = isExported;
175         }
176
177         /*
178          * Returns a char based representation of the exclusions patterns full path.
179          */
180         public char[][] fullExclusionPatternChars() {
181
182                 if (this.fullCharExclusionPatterns == UNINIT_PATTERNS) {
183                         int length = this.exclusionPatterns.length;
184                         this.fullCharExclusionPatterns = new char[length][];
185                         IPath prefixPath = this.path.removeTrailingSeparator();
186                         for (int i = 0; i < length; i++) {
187                                 this.fullCharExclusionPatterns[i] = prefixPath.append(
188                                                 this.exclusionPatterns[i]).toString().toCharArray();
189                         }
190                 }
191                 return this.fullCharExclusionPatterns;
192         }
193
194         /*
195          * Returns a char based representation of the exclusions patterns full path.
196          */
197         public char[][] fullInclusionPatternChars() {
198
199                 if (this.fullCharInclusionPatterns == UNINIT_PATTERNS) {
200                         int length = this.inclusionPatterns.length;
201                         this.fullCharInclusionPatterns = new char[length][];
202                         IPath prefixPath = this.path.removeTrailingSeparator();
203                         for (int i = 0; i < length; i++) {
204                                 this.fullCharInclusionPatterns[i] = prefixPath.append(
205                                                 this.inclusionPatterns[i]).toString().toCharArray();
206                         }
207                 }
208                 return this.fullCharInclusionPatterns;
209         }
210
211         /**
212          * Returns the XML encoding of the class path.
213          */
214         public Element elementEncode(Document document, IPath projectPath)
215                         throws JavaModelException {
216
217                 Element element = document.createElement("classpathentry"); //$NON-NLS-1$
218                 element.setAttribute("kind", kindToString(this.entryKind)); //$NON-NLS-1$
219                 IPath xmlPath = this.path;
220                 if (this.entryKind != IClasspathEntry.CPE_VARIABLE
221                                 && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
222                         // translate to project relative from absolute (unless a device
223                         // path)
224                         if (xmlPath.isAbsolute()) {
225                                 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
226                                         if (xmlPath.segment(0).equals(projectPath.segment(0))) {
227                                                 xmlPath = xmlPath.removeFirstSegments(1);
228                                                 xmlPath = xmlPath.makeRelative();
229                                         } else {
230                                                 xmlPath = xmlPath.makeAbsolute();
231                                         }
232                                 }
233                         }
234                 }
235                 element.setAttribute("path", xmlPath.toString()); //$NON-NLS-1$
236                 if (this.sourceAttachmentPath != null) {
237                         element.setAttribute(
238                                         "sourcepath", this.sourceAttachmentPath.toString()); //$NON-NLS-1$
239                 }
240                 if (this.sourceAttachmentRootPath != null) {
241                         element.setAttribute(
242                                         "rootpath", this.sourceAttachmentRootPath.toString()); //$NON-NLS-1$
243                 }
244                 if (this.isExported) {
245                         element.setAttribute("exported", "true"); //$NON-NLS-1$ //$NON-NLS-2$
246                 }
247
248                 if (this.exclusionPatterns.length > 0) {
249                         StringBuffer excludeRule = new StringBuffer(10);
250                         for (int i = 0, max = this.exclusionPatterns.length; i < max; i++) {
251                                 if (i > 0)
252                                         excludeRule.append('|');
253                                 excludeRule.append(this.exclusionPatterns[i]);
254                         }
255                         element.setAttribute("excluding", excludeRule.toString()); //$NON-NLS-1$
256                 }
257
258                 if (this.specificOutputLocation != null) {
259                         IPath outputLocation = this.specificOutputLocation
260                                         .removeFirstSegments(1);
261                         outputLocation = outputLocation.makeRelative();
262                         element.setAttribute("output", outputLocation.toString()); //$NON-NLS-1$ 
263                 }
264                 return element;
265         }
266
267         public static IClasspathEntry elementDecode(Element element,
268                         IJavaProject project) {
269
270                 IPath projectPath = project.getProject().getFullPath();
271                 String kindAttr = element.getAttribute("kind"); //$NON-NLS-1$
272                 String pathAttr = element.getAttribute("path"); //$NON-NLS-1$
273
274                 // ensure path is absolute
275                 IPath path = new Path(pathAttr);
276                 int kind = kindFromString(kindAttr);
277                 if (kind != IClasspathEntry.CPE_VARIABLE
278                                 && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
279                         path = projectPath.append(path);
280                 }
281                 // source attachment info (optional)
282                 IPath sourceAttachmentPath = element.hasAttribute("sourcepath") //$NON-NLS-1$
283                 ? new Path(element.getAttribute("sourcepath")) //$NON-NLS-1$
284                                 : null;
285                 IPath sourceAttachmentRootPath = element.hasAttribute("rootpath") //$NON-NLS-1$
286                 ? new Path(element.getAttribute("rootpath")) //$NON-NLS-1$
287                                 : null;
288
289                 // exported flag (optional)
290                 boolean isExported = element.getAttribute("exported").equals("true"); //$NON-NLS-1$ //$NON-NLS-2$
291
292                 // exclusion patterns (optional)
293                 String exclusion = element.getAttribute("excluding"); //$NON-NLS-1$ 
294                 IPath[] exclusionPatterns = ClasspathEntry.NO_EXCLUSION_PATTERNS;
295                 if (!exclusion.equals("")) { //$NON-NLS-1$ 
296                         char[][] patterns = CharOperation.splitOn('|', exclusion
297                                         .toCharArray());
298                         int patternCount;
299                         if ((patternCount = patterns.length) > 0) {
300                                 exclusionPatterns = new IPath[patternCount];
301                                 for (int j = 0; j < patterns.length; j++) {
302                                         exclusionPatterns[j] = new Path(new String(patterns[j]));
303                                 }
304                         }
305                 }
306
307                 // custom output location
308                 IPath outputLocation = element.hasAttribute("output") ? projectPath.append(element.getAttribute("output")) : null; //$NON-NLS-1$ //$NON-NLS-2$
309
310                 // recreate the CP entry
311                 switch (kind) {
312
313                 case IClasspathEntry.CPE_PROJECT:
314                         return JavaCore.newProjectEntry(path, isExported);
315
316                         // case IClasspathEntry.CPE_LIBRARY :
317                         // return JavaCore.newLibraryEntry(
318                         // path,
319                         // sourceAttachmentPath,
320                         // sourceAttachmentRootPath,
321                         // isExported);
322
323                 case IClasspathEntry.CPE_SOURCE:
324                         // must be an entry in this project or specify another project
325                         String projSegment = path.segment(0);
326                         if (projSegment != null
327                                         && projSegment.equals(project.getElementName())) { // this
328                                                                                                                                                 // project
329                                 return JavaCore.newSourceEntry(path, exclusionPatterns,
330                                                 outputLocation);
331                         } else { // another project
332                                 return JavaCore.newProjectEntry(path, isExported);
333                         }
334
335                         // case IClasspathEntry.CPE_VARIABLE :
336                         // return PHPCore.newVariableEntry(
337                         // path,
338                         // sourceAttachmentPath,
339                         // sourceAttachmentRootPath,
340                         // isExported);
341
342                 case IClasspathEntry.CPE_CONTAINER:
343                         return JavaCore.newContainerEntry(path, isExported);
344
345                 case ClasspathEntry.K_OUTPUT:
346                         if (!path.isAbsolute())
347                                 return null;
348                         return new ClasspathEntry(ClasspathEntry.K_OUTPUT,
349                                         IClasspathEntry.CPE_LIBRARY, path,
350                                         ClasspathEntry.INCLUDE_ALL, ClasspathEntry.EXCLUDE_NONE,
351                                         null, // source attachment
352                                         null, // source attachment root
353                                         null, // custom output location
354                                         false);
355
356                 default:
357                         throw new Assert.AssertionFailedException(Util.bind(
358                                         "classpath.unknownKind", kindAttr)); //$NON-NLS-1$
359                 }
360         }
361
362         /**
363          * Returns true if the given object is a classpath entry with equivalent
364          * attributes.
365          */
366         public boolean equals(Object object) {
367                 if (this == object)
368                         return true;
369                 if (object instanceof IClasspathEntry) {
370                         IClasspathEntry otherEntry = (IClasspathEntry) object;
371
372                         if (this.contentKind != otherEntry.getContentKind())
373                                 return false;
374
375                         if (this.entryKind != otherEntry.getEntryKind())
376                                 return false;
377
378                         if (this.isExported != otherEntry.isExported())
379                                 return false;
380
381                         if (!this.path.equals(otherEntry.getPath()))
382                                 return false;
383
384                         IPath otherPath = otherEntry.getSourceAttachmentPath();
385                         if (this.sourceAttachmentPath == null) {
386                                 if (otherPath != null)
387                                         return false;
388                         } else {
389                                 if (!this.sourceAttachmentPath.equals(otherPath))
390                                         return false;
391                         }
392
393                         otherPath = otherEntry.getSourceAttachmentRootPath();
394                         if (this.sourceAttachmentRootPath == null) {
395                                 if (otherPath != null)
396                                         return false;
397                         } else {
398                                 if (!this.sourceAttachmentRootPath.equals(otherPath))
399                                         return false;
400                         }
401
402                         IPath[] otherIncludes = otherEntry.getInclusionPatterns();
403                         if (this.inclusionPatterns != otherIncludes) {
404                                 if (this.inclusionPatterns == null)
405                                         return false;
406                                 int includeLength = this.inclusionPatterns.length;
407                                 if (otherIncludes == null
408                                                 || otherIncludes.length != includeLength)
409                                         return false;
410                                 for (int i = 0; i < includeLength; i++) {
411                                         // compare toStrings instead of IPaths
412                                         // since IPath.equals is specified to ignore trailing
413                                         // separators
414                                         if (!this.inclusionPatterns[i].toString().equals(
415                                                         otherIncludes[i].toString()))
416                                                 return false;
417                                 }
418                         }
419
420                         IPath[] otherExcludes = otherEntry.getExclusionPatterns();
421                         if (this.exclusionPatterns != otherExcludes) {
422                                 int excludeLength = this.exclusionPatterns.length;
423                                 if (otherExcludes.length != excludeLength)
424                                         return false;
425                                 for (int i = 0; i < excludeLength; i++) {
426                                         // compare toStrings instead of IPaths
427                                         // since IPath.equals is specified to ignore trailing
428                                         // separators
429                                         if (!this.exclusionPatterns[i].toString().equals(
430                                                         otherExcludes[i].toString()))
431                                                 return false;
432                                 }
433                         }
434
435                         otherPath = otherEntry.getOutputLocation();
436                         if (this.specificOutputLocation == null) {
437                                 if (otherPath != null)
438                                         return false;
439                         } else {
440                                 if (!this.specificOutputLocation.equals(otherPath))
441                                         return false;
442                         }
443                         return true;
444                 } else {
445                         return false;
446                 }
447         }
448
449         /**
450          * @see IClasspathEntry
451          */
452         public int getContentKind() {
453                 return this.contentKind;
454         }
455
456         /**
457          * @see IClasspathEntry
458          */
459         public int getEntryKind() {
460                 return this.entryKind;
461         }
462
463         /**
464          * @see IClasspathEntry#getExclusionPatterns()
465          */
466         public IPath[] getExclusionPatterns() {
467                 return this.exclusionPatterns;
468         }
469
470         /**
471          * @see IClasspathEntry#getExclusionPatterns()
472          */
473         public IPath[] getInclusionPatterns() {
474                 return this.inclusionPatterns;
475         }
476
477         /**
478          * @see IClasspathEntry#getOutputLocation()
479          */
480         public IPath getOutputLocation() {
481                 return this.specificOutputLocation;
482         }
483
484         /**
485          * @see IClasspathEntry
486          */
487         public IPath getPath() {
488                 return this.path;
489         }
490
491         /**
492          * @see IClasspathEntry
493          */
494         public IPath getSourceAttachmentPath() {
495                 return this.sourceAttachmentPath;
496         }
497
498         /**
499          * @see IClasspathEntry
500          */
501         public IPath getSourceAttachmentRootPath() {
502                 return this.sourceAttachmentRootPath;
503         }
504
505         /**
506          * Returns the hash code for this classpath entry
507          */
508         public int hashCode() {
509                 return this.path.hashCode();
510         }
511
512         /**
513          * @see IClasspathEntry#isExported()
514          */
515         public boolean isExported() {
516                 return this.isExported;
517         }
518
519         /**
520          * Returns the kind of a <code>PackageFragmentRoot</code> from its
521          * <code>String</code> form.
522          */
523         static int kindFromString(String kindStr) {
524
525                 if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
526                         return IClasspathEntry.CPE_PROJECT;
527                 if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
528                         return IClasspathEntry.CPE_VARIABLE;
529                 if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
530                         return IClasspathEntry.CPE_CONTAINER;
531                 if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
532                         return IClasspathEntry.CPE_SOURCE;
533                 if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
534                         return IClasspathEntry.CPE_LIBRARY;
535                 if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
536                         return ClasspathEntry.K_OUTPUT;
537                 return -1;
538         }
539
540         /**
541          * Returns a <code>String</code> for the kind of a class path entry.
542          */
543         static String kindToString(int kind) {
544
545                 switch (kind) {
546                 case IClasspathEntry.CPE_PROJECT:
547                         return "src"; // backward compatibility //$NON-NLS-1$
548                 case IClasspathEntry.CPE_SOURCE:
549                         return "src"; //$NON-NLS-1$
550                 case IClasspathEntry.CPE_LIBRARY:
551                         return "lib"; //$NON-NLS-1$
552                 case IClasspathEntry.CPE_VARIABLE:
553                         return "var"; //$NON-NLS-1$
554                 case IClasspathEntry.CPE_CONTAINER:
555                         return "con"; //$NON-NLS-1$
556                 case ClasspathEntry.K_OUTPUT:
557                         return "output"; //$NON-NLS-1$
558                 default:
559                         return "unknown"; //$NON-NLS-1$
560                 }
561         }
562
563         /**
564          * Returns a printable representation of this classpath entry.
565          */
566         public String toString() {
567                 StringBuffer buffer = new StringBuffer();
568                 buffer.append(getPath().toString());
569                 buffer.append('[');
570                 switch (getEntryKind()) {
571                 case IClasspathEntry.CPE_LIBRARY:
572                         buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
573                         break;
574                 case IClasspathEntry.CPE_PROJECT:
575                         buffer.append("CPE_PROJECT"); //$NON-NLS-1$
576                         break;
577                 case IClasspathEntry.CPE_SOURCE:
578                         buffer.append("CPE_SOURCE"); //$NON-NLS-1$
579                         break;
580                 case IClasspathEntry.CPE_VARIABLE:
581                         buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
582                         break;
583                 case IClasspathEntry.CPE_CONTAINER:
584                         buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
585                         break;
586                 }
587                 buffer.append("]["); //$NON-NLS-1$
588                 switch (getContentKind()) {
589                 case IPackageFragmentRoot.K_BINARY:
590                         buffer.append("K_BINARY"); //$NON-NLS-1$
591                         break;
592                 case IPackageFragmentRoot.K_SOURCE:
593                         buffer.append("K_SOURCE"); //$NON-NLS-1$
594                         break;
595                 case ClasspathEntry.K_OUTPUT:
596                         buffer.append("K_OUTPUT"); //$NON-NLS-1$
597                         break;
598                 }
599                 buffer.append(']');
600                 if (getSourceAttachmentPath() != null) {
601                         buffer.append("[sourcePath:"); //$NON-NLS-1$
602                         buffer.append(getSourceAttachmentPath());
603                         buffer.append(']');
604                 }
605                 if (getSourceAttachmentRootPath() != null) {
606                         buffer.append("[rootPath:"); //$NON-NLS-1$
607                         buffer.append(getSourceAttachmentRootPath());
608                         buffer.append(']');
609                 }
610                 buffer.append("[isExported:"); //$NON-NLS-1$
611                 buffer.append(this.isExported);
612                 buffer.append(']');
613                 IPath[] patterns = getExclusionPatterns();
614                 int length;
615                 if ((length = patterns.length) > 0) {
616                         buffer.append("[excluding:"); //$NON-NLS-1$
617                         for (int i = 0; i < length; i++) {
618                                 buffer.append(patterns[i]);
619                                 if (i != length - 1) {
620                                         buffer.append('|');
621                                 }
622                         }
623                         buffer.append(']');
624                 }
625                 if (getOutputLocation() != null) {
626                         buffer.append("[output:"); //$NON-NLS-1$
627                         buffer.append(getOutputLocation());
628                         buffer.append(']');
629                 }
630                 return buffer.toString();
631         }
632
633         /**
634          * Answers an ID which is used to distinguish entries during package
635          * fragment root computations
636          */
637         public String rootID() {
638
639                 if (this.rootID == null) {
640                         switch (this.entryKind) {
641                         case IClasspathEntry.CPE_LIBRARY:
642                                 this.rootID = "[LIB]" + this.path; //$NON-NLS-1$
643                                 break;
644                         case IClasspathEntry.CPE_PROJECT:
645                                 this.rootID = "[PRJ]" + this.path; //$NON-NLS-1$
646                                 break;
647                         case IClasspathEntry.CPE_SOURCE:
648                                 this.rootID = "[SRC]" + this.path; //$NON-NLS-1$
649                                 break;
650                         case IClasspathEntry.CPE_VARIABLE:
651                                 this.rootID = "[VAR]" + this.path; //$NON-NLS-1$
652                                 break;
653                         case IClasspathEntry.CPE_CONTAINER:
654                                 this.rootID = "[CON]" + this.path; //$NON-NLS-1$
655                                 break;
656                         default:
657                                 this.rootID = ""; //$NON-NLS-1$
658                                 break;
659                         }
660                 }
661                 return this.rootID;
662         }
663
664         /**
665          * @see IClasspathEntry
666          * @deprecated
667          */
668         public IClasspathEntry getResolvedEntry() {
669
670                 return JavaCore.getResolvedClasspathEntry(this);
671         }
672
673         /**
674          * Returns the XML encoding of the class path.
675          */
676         public void elementEncode(XMLWriter writer, IPath projectPath,
677                         boolean indent, boolean newLine) {
678                 HashMap parameters = new HashMap();
679
680                 parameters.put("kind", ClasspathEntry.kindToString(this.entryKind));//$NON-NLS-1$
681
682                 IPath xmlPath = this.path;
683                 if (this.entryKind != IClasspathEntry.CPE_VARIABLE
684                                 && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
685                         // translate to project relative from absolute (unless a device
686                         // path)
687                         if (xmlPath.isAbsolute()) {
688                                 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
689                                         if (xmlPath.segment(0).equals(projectPath.segment(0))) {
690                                                 xmlPath = xmlPath.removeFirstSegments(1);
691                                                 xmlPath = xmlPath.makeRelative();
692                                         } else {
693                                                 xmlPath = xmlPath.makeAbsolute();
694                                         }
695                                 }
696                         }
697                 }
698                 parameters.put("path", String.valueOf(xmlPath));//$NON-NLS-1$
699
700                 if (this.sourceAttachmentPath != null) {
701                         xmlPath = this.sourceAttachmentPath;
702                         // translate to project relative from absolute
703                         if (this.entryKind != IClasspathEntry.CPE_VARIABLE
704                                         && projectPath != null && projectPath.isPrefixOf(xmlPath)) {
705                                 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
706                                         xmlPath = xmlPath.removeFirstSegments(1);
707                                         xmlPath = xmlPath.makeRelative();
708                                 }
709                         }
710                         parameters.put("sourcepath", String.valueOf(xmlPath));//$NON-NLS-1$
711                 }
712                 if (this.sourceAttachmentRootPath != null) {
713                         parameters.put(
714                                         "rootpath", String.valueOf(this.sourceAttachmentRootPath));//$NON-NLS-1$
715                 }
716                 if (this.isExported) {
717                         parameters.put("exported", "true");//$NON-NLS-1$//$NON-NLS-2$
718                 }
719                 // if (this.inclusionPatterns != null && this.inclusionPatterns.length >
720                 // 0) {
721                 // StringBuffer includeRule = new StringBuffer(10);
722                 // for (int i = 0, max = this.inclusionPatterns.length; i < max; i++){
723                 // if (i > 0) includeRule.append('|');
724                 // includeRule.append(this.inclusionPatterns[i]);
725                 // }
726                 // parameters.put("including",
727                 // String.valueOf(includeRule));//$NON-NLS-1$
728                 // }
729                 if (this.exclusionPatterns != null && this.exclusionPatterns.length > 0) {
730                         StringBuffer excludeRule = new StringBuffer(10);
731                         for (int i = 0, max = this.exclusionPatterns.length; i < max; i++) {
732                                 if (i > 0)
733                                         excludeRule.append('|');
734                                 excludeRule.append(this.exclusionPatterns[i]);
735                         }
736                         parameters.put("excluding", String.valueOf(excludeRule));//$NON-NLS-1$
737                 }
738
739                 if (this.specificOutputLocation != null) {
740                         IPath outputLocation = this.specificOutputLocation
741                                         .removeFirstSegments(1);
742                         outputLocation = outputLocation.makeRelative();
743                         parameters.put("output", String.valueOf(outputLocation));//$NON-NLS-1$
744                 }
745
746                 writer.printTag("classpathentry", parameters, indent, newLine, true);//$NON-NLS-1$
747         }
748
749         /**
750          * Validate a given classpath and output location for a project, using the
751          * following rules:
752          * <ul>
753          * <li> Classpath entries cannot collide with each other; that is, all entry
754          * paths must be unique.
755          * <li> The project output location path cannot be null, must be absolute
756          * and located inside the project.
757          * <li> Specific output locations (specified on source entries) can be null,
758          * if not they must be located inside the project,
759          * <li> A project entry cannot refer to itself directly (that is, a project
760          * cannot prerequisite itself).
761          * <li> Classpath entries or output locations cannot coincidate or be nested
762          * in each other, except for the following scenarii listed below:
763          * <ul>
764          * <li> A source folder can coincidate with its own output location, in
765          * which case this output can then contain library archives. However, a
766          * specific output location cannot coincidate with any library or a distinct
767          * source folder than the one referring to it. </li>
768          * <li> A source/library folder can be nested in any source folder as long
769          * as the nested folder is excluded from the enclosing one. </li>
770          * <li> An output location can be nested in a source folder, if the source
771          * folder coincidates with the project itself, or if the output location is
772          * excluded from the source folder. </li>
773          * </ul>
774          * </ul>
775          * 
776          * Note that the classpath entries are not validated automatically. Only
777          * bound variables or containers are considered in the checking process
778          * (this allows to perform a consistency check on a classpath which has
779          * references to yet non existing projects, folders, ...).
780          * <p>
781          * This validation is intended to anticipate classpath issues prior to
782          * assigning it to a project. In particular, it will automatically be
783          * performed during the classpath setting operation (if validation fails,
784          * the classpath setting will not complete).
785          * <p>
786          * 
787          * @param javaProject
788          *            the given java project
789          * @param rawClasspath
790          *            a given classpath
791          * @param projectOutputLocation
792          *            a given output location
793          * @return a status object with code <code>IStatus.OK</code> if the given
794          *         classpath and output location are compatible, otherwise a status
795          *         object indicating what is wrong with the classpath or output
796          *         location
797          */
798         public static IJavaModelStatus validateClasspath(IJavaProject javaProject,
799                         IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {
800
801                 IProject project = javaProject.getProject();
802                 IPath projectPath = project.getFullPath();
803                 String projectName = javaProject.getElementName();
804
805                 /* validate output location */
806                 if (projectOutputLocation == null) {
807                         return new JavaModelStatus(IJavaModelStatusConstants.NULL_PATH);
808                 }
809                 if (projectOutputLocation.isAbsolute()) {
810                         if (!projectPath.isPrefixOf(projectOutputLocation)) {
811                                 return new JavaModelStatus(
812                                                 IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT,
813                                                 javaProject, projectOutputLocation.toString());
814                         }
815                 } else {
816                         return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH,
817                                         projectOutputLocation);
818                 }
819
820                 boolean hasSource = false;
821                 boolean hasLibFolder = false;
822
823                 // tolerate null path, it will be reset to default
824                 if (rawClasspath == null)
825                         return JavaModelStatus.VERIFIED_OK;
826
827                 // retrieve resolved classpath
828                 IClasspathEntry[] classpath;
829                 try {
830                         classpath = ((JavaProject) javaProject).getResolvedClasspath(
831                                         rawClasspath, null /* output */, true/* ignore pb */,
832                                         false/* no marker */, null /* no reverse map */);
833                 } catch (JavaModelException e) {
834                         return e.getJavaModelStatus();
835                 }
836                 int length = classpath.length;
837
838                 int outputCount = 1;
839                 IPath[] outputLocations = new IPath[length + 1];
840                 boolean[] allowNestingInOutputLocations = new boolean[length + 1];
841                 outputLocations[0] = projectOutputLocation;
842
843                 // retrieve and check output locations
844                 IPath potentialNestedOutput = null; // for error reporting purpose
845                 int sourceEntryCount = 0;
846                 boolean disableExclusionPatterns = JavaCore.DISABLED.equals(javaProject
847                                 .getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS,
848                                                 true));
849                 boolean disableCustomOutputLocations = JavaCore.DISABLED
850                                 .equals(javaProject
851                                                 .getOption(
852                                                                 JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS,
853                                                                 true));
854
855                 for (int i = 0; i < length; i++) {
856                         IClasspathEntry resolvedEntry = classpath[i];
857                         switch (resolvedEntry.getEntryKind()) {
858                         case IClasspathEntry.CPE_SOURCE:
859                                 sourceEntryCount++;
860
861                                 if (disableExclusionPatterns
862                                                 && ((resolvedEntry.getInclusionPatterns() != null && resolvedEntry
863                                                                 .getInclusionPatterns().length > 0) || (resolvedEntry
864                                                                 .getExclusionPatterns() != null && resolvedEntry
865                                                                 .getExclusionPatterns().length > 0))) {
866                                         return new JavaModelStatus(
867                                                         IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS,
868                                                         javaProject, resolvedEntry.getPath());
869                                 }
870                                 IPath customOutput;
871                                 if ((customOutput = resolvedEntry.getOutputLocation()) != null) {
872
873                                         if (disableCustomOutputLocations) {
874                                                 return new JavaModelStatus(
875                                                                 IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS,
876                                                                 javaProject, resolvedEntry.getPath());
877                                         }
878                                         // ensure custom output is in project
879                                         if (customOutput.isAbsolute()) {
880                                                 if (!javaProject.getPath().isPrefixOf(customOutput)) {
881                                                         return new JavaModelStatus(
882                                                                         IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT,
883                                                                         javaProject, customOutput.toString());
884                                                 }
885                                         } else {
886                                                 return new JavaModelStatus(
887                                                                 IJavaModelStatusConstants.RELATIVE_PATH,
888                                                                 customOutput);
889                                         }
890
891                                         // ensure custom output doesn't conflict with other outputs
892                                         // check exact match
893                                         if (Util.indexOfMatchingPath(customOutput, outputLocations,
894                                                         outputCount) != -1) {
895                                                 continue; // already found
896                                         }
897                                         // accumulate all outputs, will check nesting once all
898                                         // available (to handle ordering issues)
899                                         outputLocations[outputCount++] = customOutput;
900                                 }
901                         }
902                 }
903                 // check nesting across output locations
904                 for (int i = 1 /* no check for default output */; i < outputCount; i++) {
905                         IPath customOutput = outputLocations[i];
906                         int index;
907                         // check nesting
908                         if ((index = Util.indexOfEnclosingPath(customOutput,
909                                         outputLocations, outputCount)) != -1
910                                         && index != i) {
911                                 if (index == 0) {
912                                         // custom output is nested in project's output: need to
913                                         // check if all source entries have a custom
914                                         // output before complaining
915                                         if (potentialNestedOutput == null)
916                                                 potentialNestedOutput = customOutput;
917                                 } else {
918                                         return new JavaModelStatus(
919                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
920                                                         Util
921                                                                         .bind(
922                                                                                         "classpath.cannotNestOutputInOutput", customOutput.makeRelative().toString(), outputLocations[index].makeRelative().toString())); //$NON-NLS-1$
923                                 }
924                         }
925                 }
926                 // allow custom output nesting in project's output if all source entries
927                 // have a custom output
928                 if (sourceEntryCount <= outputCount - 1) {
929                         allowNestingInOutputLocations[0] = true;
930                 } else if (potentialNestedOutput != null) {
931                         return new JavaModelStatus(
932                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
933                                         Util
934                                                         .bind(
935                                                                         "classpath.cannotNestOutputInOutput", potentialNestedOutput.makeRelative().toString(), outputLocations[0].makeRelative().toString())); //$NON-NLS-1$
936                 }
937
938                 for (int i = 0; i < length; i++) {
939                         IClasspathEntry resolvedEntry = classpath[i];
940                         IPath path = resolvedEntry.getPath();
941                         int index;
942                         switch (resolvedEntry.getEntryKind()) {
943
944                         case IClasspathEntry.CPE_SOURCE:
945                                 hasSource = true;
946                                 if ((index = Util.indexOfMatchingPath(path, outputLocations,
947                                                 outputCount)) != -1) {
948                                         allowNestingInOutputLocations[index] = true;
949                                 }
950                                 break;
951
952                         // case IClasspathEntry.CPE_LIBRARY:
953                         // hasLibFolder |=
954                         // !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(path.lastSegment());
955                         // if ((index = Util.indexOfMatchingPath(path, outputLocations,
956                         // outputCount)) != -1){
957                         // allowNestingInOutputLocations[index] = true;
958                         // }
959                         // break;
960                         }
961                 }
962                 if (!hasSource && !hasLibFolder) { // if no source and no lib folder,
963                                                                                         // then allowed
964                         for (int i = 0; i < outputCount; i++)
965                                 allowNestingInOutputLocations[i] = true;
966                 }
967
968                 HashSet pathes = new HashSet(length);
969
970                 // check all entries
971                 for (int i = 0; i < length; i++) {
972                         IClasspathEntry entry = classpath[i];
973                         if (entry == null)
974                                 continue;
975                         IPath entryPath = entry.getPath();
976                         int kind = entry.getEntryKind();
977
978                         // Build some common strings for status message
979                         boolean isProjectRelative = entryPath.segment(0).toString().equals(
980                                         projectName);
981                         String entryPathMsg = isProjectRelative ? entryPath
982                                         .removeFirstSegments(1).toString() : entryPath
983                                         .makeRelative().toString();
984
985                         // complain if duplicate path
986                         if (!pathes.add(entryPath)) {
987                                 return new JavaModelStatus(
988                                                 IJavaModelStatusConstants.NAME_COLLISION,
989                                                 Util
990                                                                 .bind(
991                                                                                 "classpath.duplicateEntryPath", entryPathMsg, projectName)); //$NON-NLS-1$
992                         }
993                         // no further check if entry coincidates with project or output
994                         // location
995                         if (entryPath.equals(projectPath)) {
996                                 // complain if self-referring project entry
997                                 if (kind == IClasspathEntry.CPE_PROJECT) {
998                                         return new JavaModelStatus(
999                                                         IJavaModelStatusConstants.INVALID_PATH,
1000                                                         Util
1001                                                                         .bind(
1002                                                                                         "classpath.cannotReferToItself", entryPath.makeRelative().toString()));//$NON-NLS-1$
1003                                 }
1004                                 // tolerate nesting output in src if src==prj
1005                                 continue;
1006                         }
1007
1008                         // allow nesting source entries in each other as long as the outer
1009                         // entry excludes the inner one
1010                         if (kind == IClasspathEntry.CPE_SOURCE) {
1011                                 // || (kind == IClasspathEntry.CPE_LIBRARY &&
1012                                 // !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(entryPath.lastSegment()))){
1013                                 for (int j = 0; j < classpath.length; j++) {
1014                                         IClasspathEntry otherEntry = classpath[j];
1015                                         if (otherEntry == null)
1016                                                 continue;
1017                                         int otherKind = otherEntry.getEntryKind();
1018                                         IPath otherPath = otherEntry.getPath();
1019                                         if (entry != otherEntry
1020                                                         && (otherKind == IClasspathEntry.CPE_SOURCE)) {
1021                                                 // || (otherKind == IClasspathEntry.CPE_LIBRARY
1022                                                 // &&
1023                                                 // !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(otherPath.lastSegment())))){
1024                                                 char[][] inclusionPatterns, exclusionPatterns;
1025                                                 if (otherPath.isPrefixOf(entryPath)
1026                                                                 && !otherPath.equals(entryPath)
1027                                                                 && !Util
1028                                                                                 .isExcluded(
1029                                                                                                 entryPath.append("*"), inclusionPatterns = ((ClasspathEntry) otherEntry).fullInclusionPatternChars(), exclusionPatterns = ((ClasspathEntry) otherEntry).fullExclusionPatternChars(), false)) { //$NON-NLS-1$
1030                                                         String exclusionPattern = entryPath
1031                                                                         .removeFirstSegments(
1032                                                                                         otherPath.segmentCount())
1033                                                                         .segment(0);
1034                                                         if (Util.isExcluded(entryPath, inclusionPatterns,
1035                                                                         exclusionPatterns, false)) {
1036                                                                 return new JavaModelStatus(
1037                                                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1038                                                                                 Util
1039                                                                                                 .bind(
1040                                                                                                                 "classpath.mustEndWithSlash", exclusionPattern, entryPath.makeRelative().toString())); //$NON-NLS-1$
1041                                                         } else {
1042                                                                 if (otherKind == IClasspathEntry.CPE_SOURCE) {
1043                                                                         exclusionPattern += '/';
1044                                                                         return new JavaModelStatus(
1045                                                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1046                                                                                         Util
1047                                                                                                         .bind(
1048                                                                                                                         "classpath.cannotNestEntryInEntry", new String[] { entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString(), exclusionPattern })); //$NON-NLS-1$
1049                                                                 } else {
1050                                                                         return new JavaModelStatus(
1051                                                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1052                                                                                         Util
1053                                                                                                         .bind(
1054                                                                                                                         "classpath.cannotNestEntryInLibrary", entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString())); //$NON-NLS-1$
1055                                                                 }
1056                                                         }
1057                                                 }
1058                                         }
1059                                 }
1060                         }
1061
1062                         // prevent nesting output location inside entry unless enclosing is
1063                         // a source entry which explicitly exclude the output location
1064                         char[][] inclusionPatterns = ((ClasspathEntry) entry)
1065                                         .fullInclusionPatternChars();
1066                         char[][] exclusionPatterns = ((ClasspathEntry) entry)
1067                                         .fullExclusionPatternChars();
1068                         for (int j = 0; j < outputCount; j++) {
1069                                 IPath currentOutput = outputLocations[j];
1070                                 if (entryPath.equals(currentOutput))
1071                                         continue;
1072                                 if (entryPath.isPrefixOf(currentOutput)) {
1073                                         if (kind != IClasspathEntry.CPE_SOURCE
1074                                                         || !Util.isExcluded(currentOutput,
1075                                                                         inclusionPatterns, exclusionPatterns, true)) {
1076                                                 return new JavaModelStatus(
1077                                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1078                                                                 Util
1079                                                                                 .bind(
1080                                                                                                 "classpath.cannotNestOutputInEntry", currentOutput.makeRelative().toString(), entryPath.makeRelative().toString())); //$NON-NLS-1$
1081                                         }
1082                                 }
1083                         }
1084
1085                         // prevent nesting entry inside output location - when distinct from
1086                         // project or a source folder
1087                         for (int j = 0; j < outputCount; j++) {
1088                                 if (allowNestingInOutputLocations[j])
1089                                         continue;
1090                                 IPath currentOutput = outputLocations[j];
1091                                 if (currentOutput.isPrefixOf(entryPath)) {
1092                                         return new JavaModelStatus(
1093                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1094                                                         Util
1095                                                                         .bind(
1096                                                                                         "classpath.cannotNestEntryInOutput", entryPath.makeRelative().toString(), currentOutput.makeRelative().toString())); //$NON-NLS-1$
1097                                 }
1098                         }
1099                 }
1100                 // ensure that no specific output is coincidating with another source
1101                 // folder (only allowed if matching current source folder)
1102                 // 36465 - for 2.0 backward compatibility, only check specific output
1103                 // locations (the default can still coincidate)
1104                 // perform one separate iteration so as to not take precedence over
1105                 // previously checked scenarii (in particular should
1106                 // diagnose nesting source folder issue before this one, for example,
1107                 // [src]"Project/", [src]"Project/source/" and output="Project/" should
1108                 // first complain about missing exclusion pattern
1109                 for (int i = 0; i < length; i++) {
1110                         IClasspathEntry entry = classpath[i];
1111                         if (entry == null)
1112                                 continue;
1113                         IPath entryPath = entry.getPath();
1114                         int kind = entry.getEntryKind();
1115
1116                         // Build some common strings for status message
1117                         boolean isProjectRelative = entryPath.segment(0).toString().equals(
1118                                         projectName);
1119                         String entryPathMsg = isProjectRelative ? entryPath
1120                                         .removeFirstSegments(1).toString() : entryPath
1121                                         .makeRelative().toString();
1122
1123                         if (kind == IClasspathEntry.CPE_SOURCE) {
1124                                 IPath output = entry.getOutputLocation();
1125                                 if (output == null)
1126                                         continue; // 36465 - for 2.0 backward compatibility, only
1127                                                                 // check specific output locations (the default
1128                                                                 // can still coincidate)
1129                                 // if (output == null) output = projectOutputLocation; // if no
1130                                 // specific output, still need to check using default output
1131                                 // (this line would check default output)
1132                                 for (int j = 0; j < length; j++) {
1133                                         IClasspathEntry otherEntry = classpath[j];
1134                                         if (otherEntry == entry)
1135                                                 continue;
1136
1137                                         // Build some common strings for status message
1138                                         boolean opStartsWithProject = otherEntry.getPath().segment(
1139                                                         0).toString().equals(projectName);
1140                                         String otherPathMsg = opStartsWithProject ? otherEntry
1141                                                         .getPath().removeFirstSegments(1).toString()
1142                                                         : otherEntry.getPath().makeRelative().toString();
1143
1144                                         switch (otherEntry.getEntryKind()) {
1145                                         case IClasspathEntry.CPE_SOURCE:
1146                                                 if (otherEntry.getPath().equals(output)) {
1147                                                         return new JavaModelStatus(
1148                                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1149                                                                         Util
1150                                                                                         .bind(
1151                                                                                                         "classpath.cannotUseDistinctSourceFolderAsOutput", new String[] { entryPathMsg, otherPathMsg, projectName })); //$NON-NLS-1$
1152                                                 }
1153                                                 break;
1154                                         case IClasspathEntry.CPE_LIBRARY:
1155                                                 if (otherEntry.getPath().equals(output)) {
1156                                                         return new JavaModelStatus(
1157                                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1158                                                                         Util
1159                                                                                         .bind(
1160                                                                                                         "classpath.cannotUseLibraryAsOutput", new String[] { entryPathMsg, otherPathMsg, projectName })); //$NON-NLS-1$
1161                                                 }
1162                                         }
1163                                 }
1164                         }
1165                 }
1166                 return JavaModelStatus.VERIFIED_OK;
1167         }
1168
1169         /**
1170          * Returns a Java model status describing the problem related to this
1171          * classpath entry if any, a status object with code <code>IStatus.OK</code>
1172          * if the entry is fine (that is, if the given classpath entry denotes a
1173          * valid element to be referenced onto a classpath).
1174          * 
1175          * @param project
1176          *            the given java project
1177          * @param entry
1178          *            the given classpath entry
1179          * @param checkSourceAttachment
1180          *            a flag to determine if source attachement should be checked
1181          * @param recurseInContainers
1182          *            flag indicating whether validation should be applied to
1183          *            container entries recursively
1184          * @return a java model status describing the problem related to this
1185          *         classpath entry if any, a status object with code
1186          *         <code>IStatus.OK</code> if the entry is fine
1187          */
1188         public static IJavaModelStatus validateClasspathEntry(IJavaProject project,
1189                         IClasspathEntry entry, boolean checkSourceAttachment,
1190                         boolean recurseInContainers) {
1191
1192                 IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
1193                 IPath path = entry.getPath();
1194
1195                 // Build some common strings for status message
1196                 String projectName = project.getElementName();
1197                 boolean pathStartsWithProject = path.segment(0).toString().equals(
1198                                 projectName);
1199                 String entryPathMsg = pathStartsWithProject ? path.removeFirstSegments(
1200                                 1).toString() : path.makeRelative().toString();
1201
1202                 switch (entry.getEntryKind()) {
1203
1204                 // container entry check
1205                 // case IClasspathEntry.CPE_CONTAINER :
1206                 // if (path != null && path.segmentCount() >= 1){
1207                 // try {
1208                 // IClasspathContainer container =
1209                 // JavaModelManager.getJavaModelManager().getClasspathContainer(path,
1210                 // project);
1211                 // // container retrieval is performing validation check on container
1212                 // entry kinds.
1213                 // if (container == null){
1214                 // return new
1215                 // JavaModelStatus(IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND,
1216                 // project, path);
1217                 // } else if (container ==
1218                 // JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
1219                 // // don't create a marker if initialization is in progress (case of cp
1220                 // initialization batching)
1221                 // return JavaModelStatus.VERIFIED_OK;
1222                 // }
1223                 // IClasspathEntry[] containerEntries = container.getClasspathEntries();
1224                 // if (containerEntries != null){
1225                 // for (int i = 0, length = containerEntries.length; i < length; i++){
1226                 // IClasspathEntry containerEntry = containerEntries[i];
1227                 // int kind = containerEntry == null ? 0 :
1228                 // containerEntry.getEntryKind();
1229                 // if (containerEntry == null
1230                 // || kind == IClasspathEntry.CPE_SOURCE
1231                 // || kind == IClasspathEntry.CPE_VARIABLE
1232                 // || kind == IClasspathEntry.CPE_CONTAINER){
1233                 // String description = container.getDescription();
1234                 // if (description == null) description =
1235                 // path.makeRelative().toString();
1236                 // return new
1237                 // JavaModelStatus(IJavaModelStatusConstants.INVALID_CP_CONTAINER_ENTRY,
1238                 // project, path);
1239                 // }
1240                 // if (recurseInContainers) {
1241                 // IJavaModelStatus containerEntryStatus =
1242                 // validateClasspathEntry(project, containerEntry,
1243                 // checkSourceAttachment, recurseInContainers);
1244                 // if (!containerEntryStatus.isOK()){
1245                 // return containerEntryStatus;
1246                 // }
1247                 // }
1248                 // }
1249                 // }
1250                 // } catch(JavaModelException e){
1251                 // return new JavaModelStatus(e);
1252                 // }
1253                 // } else {
1254                 // return new
1255                 // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1256                 // Util.bind("classpath.illegalContainerPath", entryPathMsg,
1257                 // projectName)); //$NON-NLS-1$
1258                 // }
1259                 // break;
1260
1261                 // variable entry check
1262                 case IClasspathEntry.CPE_VARIABLE:
1263                         if (path != null && path.segmentCount() >= 1) {
1264                                 try {
1265                                         entry = JavaCore.getResolvedClasspathEntry(entry);
1266                                 } catch (Assert.AssertionFailedException e) {
1267                                         // Catch the assertion failure and throw java model
1268                                         // exception instead
1269                                         // see bug
1270                                         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
1271                                         return new JavaModelStatus(
1272                                                         IJavaModelStatusConstants.INVALID_PATH, e
1273                                                                         .getMessage());
1274                                 }
1275                                 if (entry == null) {
1276                                         return new JavaModelStatus(
1277                                                         IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND,
1278                                                         project, path);
1279                                 }
1280                                 return validateClasspathEntry(project, entry,
1281                                                 checkSourceAttachment, recurseInContainers);
1282                         } else {
1283                                 return new JavaModelStatus(
1284                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1285                                                 Util
1286                                                                 .bind(
1287                                                                                 "classpath.illegalVariablePath", path.makeRelative().toString(), projectName)); //$NON-NLS-1$
1288                         }
1289
1290                         // library entry check
1291                         // case IClasspathEntry.CPE_LIBRARY :
1292                         // if (path != null && path.isAbsolute() && !path.isEmpty()) {
1293                         // IPath sourceAttachment = entry.getSourceAttachmentPath();
1294                         // Object target = JavaModel.getTarget(workspaceRoot, path, true);
1295                         // if (target != null &&
1296                         // project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true) !=
1297                         // JavaCore.IGNORE) {
1298                         // long projectTargetJDK =
1299                         // CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
1300                         // true));
1301                         // long libraryJDK = Util.getJdkLevel(target);
1302                         // if (libraryJDK != 0 && libraryJDK > projectTargetJDK) {
1303                         // return new
1304                         // JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL,
1305                         // project, path, CompilerOptions.versionFromJdkLevel(libraryJDK));
1306                         // }
1307                         // }
1308                         // if (target instanceof IResource){
1309                         // IResource resolvedResource = (IResource) target;
1310                         // switch(resolvedResource.getType()){
1311                         // case IResource.FILE :
1312                         // if
1313                         // (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(resolvedResource.getName()))
1314                         // {
1315                         // if (checkSourceAttachment
1316                         // && sourceAttachment != null
1317                         // && !sourceAttachment.isEmpty()
1318                         // && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) ==
1319                         // null){
1320                         // return new
1321                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1322                         // Util.bind("classpath.unboundSourceAttachment", new String []
1323                         // {sourceAttachment.makeRelative().toString(),
1324                         // path.makeRelative().toString(), projectName})); //$NON-NLS-1$
1325                         // }
1326                         // } else {
1327                         // return new
1328                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1329                         // Util.bind("classpath.illegalLibraryArchive", entryPathMsg,
1330                         // projectName)); //$NON-NLS-1$
1331                         // }
1332                         // break;
1333                         // case IResource.FOLDER : // internal binary folder
1334                         // if (checkSourceAttachment
1335                         // && sourceAttachment != null
1336                         // && !sourceAttachment.isEmpty()
1337                         // && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) ==
1338                         // null){
1339                         // return new
1340                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1341                         // Util.bind("classpath.unboundSourceAttachment", new String []
1342                         // {sourceAttachment.makeRelative().toString(),
1343                         // path.makeRelative().toString(), projectName})); //$NON-NLS-1$
1344                         // }
1345                         // }
1346                         // } else if (target instanceof File){
1347                         // File file = (File) target;
1348                         // if (!file.isFile()) {
1349                         // return new
1350                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1351                         // Util.bind("classpath.illegalExternalFolder", path.toOSString(),
1352                         // projectName)); //$NON-NLS-1$
1353                         // } else if
1354                         // (!org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName()))
1355                         // {
1356                         // return new
1357                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1358                         // Util.bind("classpath.illegalLibraryArchive", path.toOSString(),
1359                         // projectName)); //$NON-NLS-1$
1360                         // } else if (checkSourceAttachment
1361                         // && sourceAttachment != null
1362                         // && !sourceAttachment.isEmpty()
1363                         // && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) ==
1364                         // null){
1365                         // return new
1366                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1367                         // Util.bind("classpath.unboundSourceAttachment", new String []
1368                         // {sourceAttachment.toString(), path.makeRelative().toString(),
1369                         // projectName})); //$NON-NLS-1$
1370                         // }
1371                         // } else {
1372                         // return new
1373                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1374                         // Util.bind("classpath.unboundLibrary",
1375                         // path.makeRelative().toString(), projectName)); //$NON-NLS-1$
1376                         // }
1377                         // } else {
1378                         // return new
1379                         // JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH,
1380                         // Util.bind("classpath.illegalLibraryPath",
1381                         // path.makeRelative().toString(), projectName)); //$NON-NLS-1$
1382                         // }
1383                         // break;
1384
1385                         // project entry check
1386                 case IClasspathEntry.CPE_PROJECT:
1387                         if (path != null && path.isAbsolute() && !path.isEmpty()) {
1388                                 IProject prereqProjectRsc = workspaceRoot.getProject(path
1389                                                 .segment(0));
1390                                 IJavaProject prereqProject = JavaCore.create(prereqProjectRsc);
1391                                 try {
1392                                         if (!prereqProjectRsc.exists()
1393                                                         || !prereqProjectRsc
1394                                                                         .hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
1395                                                 return new JavaModelStatus(
1396                                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1397                                                                 Util
1398                                                                                 .bind(
1399                                                                                                 "classpath.unboundProject", path.makeRelative().segment(0).toString(), projectName)); //$NON-NLS-1$
1400                                         }
1401                                         if (!prereqProjectRsc.isOpen()) {
1402                                                 return new JavaModelStatus(
1403                                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1404                                                                 Util
1405                                                                                 .bind(
1406                                                                                                 "classpath.closedProject", path.segment(0).toString())); //$NON-NLS-1$
1407                                         }
1408                                         // if
1409                                         // (project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL,
1410                                         // true) != JavaCore.IGNORE) {
1411                                         // long projectTargetJDK =
1412                                         // CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
1413                                         // true));
1414                                         // long prereqProjectTargetJDK =
1415                                         // CompilerOptions.versionToJdkLevel(prereqProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
1416                                         // true));
1417                                         // if (prereqProjectTargetJDK > projectTargetJDK) {
1418                                         // return new
1419                                         // JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL,
1420                                         // project, path,
1421                                         // CompilerOptions.versionFromJdkLevel(prereqProjectTargetJDK));
1422                                         // }
1423                                         // }
1424                                 } catch (CoreException e) {
1425                                         return new JavaModelStatus(
1426                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1427                                                         Util
1428                                                                         .bind(
1429                                                                                         "classpath.unboundProject", path.segment(0).toString(), projectName)); //$NON-NLS-1$
1430                                 }
1431                         } else {
1432                                 return new JavaModelStatus(
1433                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1434                                                 Util
1435                                                                 .bind(
1436                                                                                 "classpath.illegalProjectPath", path.segment(0).toString(), projectName)); //$NON-NLS-1$
1437                         }
1438                         break;
1439
1440                 // project source folder
1441                 case IClasspathEntry.CPE_SOURCE:
1442                         if (((entry.getInclusionPatterns() != null && entry
1443                                         .getInclusionPatterns().length > 0) || (entry
1444                                         .getExclusionPatterns() != null && entry
1445                                         .getExclusionPatterns().length > 0))
1446                                         && JavaCore.DISABLED.equals(project.getOption(
1447                                                         JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS,
1448                                                         true))) {
1449                                 return new JavaModelStatus(
1450                                                 IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS,
1451                                                 project, path);
1452                         }
1453                         if (entry.getOutputLocation() != null
1454                                         && JavaCore.DISABLED
1455                                                         .equals(project
1456                                                                         .getOption(
1457                                                                                         JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS,
1458                                                                                         true))) {
1459                                 return new JavaModelStatus(
1460                                                 IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS,
1461                                                 project, path);
1462                         }
1463                         if (path != null && path.isAbsolute() && !path.isEmpty()) {
1464                                 IPath projectPath = project.getProject().getFullPath();
1465                                 if (!projectPath.isPrefixOf(path)
1466                                                 || JavaModel.getTarget(workspaceRoot, path, true) == null) {
1467                                         return new JavaModelStatus(
1468                                                         IJavaModelStatusConstants.INVALID_CLASSPATH,
1469                                                         Util
1470                                                                         .bind(
1471                                                                                         "classpath.unboundSourceFolder", entryPathMsg, projectName)); //$NON-NLS-1$
1472                                 }
1473                         } else {
1474                                 return new JavaModelStatus(
1475                                                 IJavaModelStatusConstants.INVALID_CLASSPATH,
1476                                                 Util
1477                                                                 .bind(
1478                                                                                 "classpath.illegalSourceFolderPath", entryPathMsg, projectName)); //$NON-NLS-1$
1479                         }
1480                         break;
1481                 }
1482                 return JavaModelStatus.VERIFIED_OK;
1483         }
1484 }