improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / ClasspathEntry.java
index 8237f92..6ca5c30 100644 (file)
  *******************************************************************************/
 package net.sourceforge.phpdt.internal.core;
 
+import java.io.File;
 import java.util.HashMap;
+import java.util.HashSet;
 
 import net.sourceforge.phpdt.core.IClasspathEntry;
+import net.sourceforge.phpdt.core.IJavaModelStatus;
+import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
 import net.sourceforge.phpdt.core.IJavaProject;
 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
 import net.sourceforge.phpdt.core.JavaCore;
 import net.sourceforge.phpdt.core.JavaModelException;
 import net.sourceforge.phpdt.core.compiler.CharOperation;
+import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
 import net.sourceforge.phpdt.internal.core.util.Util;
 import net.sourceforge.phpdt.internal.corext.Assert;
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
 import org.w3c.dom.Document;
@@ -66,16 +77,26 @@ public class ClasspathEntry implements IClasspathEntry {
         */
        public IPath path;
 
+       
        /**
-        * Patterns allowing to exclude portions of the resource tree denoted by this entry path.
+        * Patterns allowing to include/exclude portions of the resource tree denoted by this entry path.
         */
-       
+       public IPath[] inclusionPatterns;
+       private char[][] fullCharInclusionPatterns;
        public IPath[] exclusionPatterns;
        private char[][] fullCharExclusionPatterns;
        private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
 
        private String rootID;
+       /*
+        * Default inclusion pattern set
+        */
+       public final static IPath[] INCLUDE_ALL = {};
        
+       /*
+        * Default exclusion pattern set
+        */
+       public final static IPath[] EXCLUDE_NONE = {};
        /**
         * Default exclusion pattern set
         */
@@ -125,6 +146,7 @@ public class ClasspathEntry implements IClasspathEntry {
                int contentKind,
                int entryKind,
                IPath path,
+               IPath[] inclusionPatterns,
                IPath[] exclusionPatterns,
                IPath sourceAttachmentPath,
                IPath sourceAttachmentRootPath,
@@ -134,6 +156,12 @@ public class ClasspathEntry implements IClasspathEntry {
                this.contentKind = contentKind;
                this.entryKind = entryKind;
                this.path = path;
+               this.inclusionPatterns = inclusionPatterns;
+           if (inclusionPatterns != INCLUDE_ALL && inclusionPatterns.length > 0) {
+                       this.fullCharInclusionPatterns = UNINIT_PATTERNS;
+           } else {
+                       this.fullCharInclusionPatterns = null; // empty inclusion pattern means everything is included
+           }
                this.exclusionPatterns = exclusionPatterns;
                if (exclusionPatterns.length > 0) {
                        this.fullCharExclusionPatterns = UNINIT_PATTERNS;
@@ -152,7 +180,7 @@ public class ClasspathEntry implements IClasspathEntry {
                if (this.fullCharExclusionPatterns == UNINIT_PATTERNS) {
                        int length = this.exclusionPatterns.length;
                        this.fullCharExclusionPatterns = new char[length][];
-                       IPath prefixPath = path.removeTrailingSeparator();
+                       IPath prefixPath = this.path.removeTrailingSeparator();
                        for (int i = 0; i < length; i++) {
                                this.fullCharExclusionPatterns[i] = 
                                        prefixPath.append(this.exclusionPatterns[i]).toString().toCharArray();
@@ -161,6 +189,22 @@ public class ClasspathEntry implements IClasspathEntry {
                return this.fullCharExclusionPatterns;
        }
        
+       /*
+        * Returns a char based representation of the exclusions patterns full path.
+        */
+       public char[][] fullInclusionPatternChars() {
+
+               if (this.fullCharInclusionPatterns == UNINIT_PATTERNS) {
+                       int length = this.inclusionPatterns.length;
+                       this.fullCharInclusionPatterns = new char[length][];
+                       IPath prefixPath = this.path.removeTrailingSeparator();
+                       for (int i = 0; i < length; i++) {
+                               this.fullCharInclusionPatterns[i] = 
+                                       prefixPath.append(this.inclusionPatterns[i]).toString().toCharArray();
+                       }
+               }
+               return this.fullCharInclusionPatterns;
+       }
        /**
         * Returns the XML encoding of the class path.
         */
@@ -290,16 +334,18 @@ public class ClasspathEntry implements IClasspathEntry {
                                                isExported);
 
                        case ClasspathEntry.K_OUTPUT :
-                               if (!path.isAbsolute()) return null;
-                               return new ClasspathEntry(
-                                               ClasspathEntry.K_OUTPUT,
-                                               IClasspathEntry.CPE_LIBRARY,
-                                               path,
-                                               ClasspathEntry.NO_EXCLUSION_PATTERNS, 
-                                               null, // source attachment
-                                               null, // source attachment root
-                                               null, // custom output location
-                                               false);
+                         if (!path.isAbsolute()) return null;
+                           return new ClasspathEntry(
+                                       ClasspathEntry.K_OUTPUT,
+                                       IClasspathEntry.CPE_LIBRARY,
+                                       path,
+                                       ClasspathEntry.INCLUDE_ALL, 
+                                       ClasspathEntry.EXCLUDE_NONE, 
+                                       null, // source attachment
+                                       null, // source attachment root
+                                       null, // custom output location
+                                       false);
+                               
                        default :
                                throw new Assert.AssertionFailedException(Util.bind("classpath.unknownKind", kindAttr)); //$NON-NLS-1$
                }
@@ -345,6 +391,20 @@ public class ClasspathEntry implements IClasspathEntry {
                                        return false;
                        }
 
+                       IPath[] otherIncludes = otherEntry.getInclusionPatterns();
+                       if (this.inclusionPatterns != otherIncludes){
+                           if (this.inclusionPatterns == null) return false;
+                               int includeLength = this.inclusionPatterns.length;
+                               if (otherIncludes == null || otherIncludes.length != includeLength) 
+                                       return false;
+                               for (int i = 0; i < includeLength; i++) {
+                                       // compare toStrings instead of IPaths 
+                                       // since IPath.equals is specified to ignore trailing separators
+                                       if (!this.inclusionPatterns[i].toString().equals(otherIncludes[i].toString()))
+                                               return false;
+                               }
+                       }
+                       
                        IPath[] otherExcludes = otherEntry.getExclusionPatterns();
                        if (this.exclusionPatterns != otherExcludes){
                                int excludeLength = this.exclusionPatterns.length;
@@ -392,7 +452,12 @@ public class ClasspathEntry implements IClasspathEntry {
        public IPath[] getExclusionPatterns() {
                return this.exclusionPatterns;
        }
-
+       /**
+        * @see IClasspathEntry#getExclusionPatterns()
+        */
+       public IPath[] getInclusionPatterns() {
+               return this.inclusionPatterns;
+       }
        /**
         * @see IClasspathEntry#getOutputLocation()
         */
@@ -653,4 +718,476 @@ public class ClasspathEntry implements IClasspathEntry {
 
                writer.printTag("classpathentry", parameters, indent, newLine, true);//$NON-NLS-1$
        }
+       
+       /**
+        * Validate a given classpath and output location for a project, using the following rules:
+        * <ul>
+        *   <li> Classpath entries cannot collide with each other; that is, all entry paths must be unique.
+        *   <li> The project output location path cannot be null, must be absolute and located inside the project.
+        *   <li> Specific output locations (specified on source entries) can be null, if not they must be located inside the project,
+        *   <li> A project entry cannot refer to itself directly (that is, a project cannot prerequisite itself).
+     *   <li> Classpath entries or output locations cannot coincidate or be nested in each other, except for the following scenarii listed below:
+        *      <ul><li> A source folder can coincidate with its own output location, in which case this output can then contain library archives. 
+        *                     However, a specific output location cannot coincidate with any library or a distinct source folder than the one referring to it. </li> 
+        *              <li> A source/library folder can be nested in any source folder as long as the nested folder is excluded from the enclosing one. </li>
+        *                      <li> An output location can be nested in a source folder, if the source folder coincidates with the project itself, or if the output
+        *                                      location is excluded from the source folder. </li>
+        *      </ul>
+        * </ul>
+        * 
+        *  Note that the classpath entries are not validated automatically. Only bound variables or containers are considered 
+        *  in the checking process (this allows to perform a consistency check on a classpath which has references to
+        *  yet non existing projects, folders, ...).
+        *  <p>
+        *  This validation is intended to anticipate classpath issues prior to assigning it to a project. In particular, it will automatically
+        *  be performed during the classpath setting operation (if validation fails, the classpath setting will not complete).
+        *  <p>
+        * @param javaProject the given java project
+        * @param rawClasspath a given classpath
+        * @param projectOutputLocation a given output location
+        * @return a status object with code <code>IStatus.OK</code> if
+        *              the given classpath and output location are compatible, otherwise a status 
+        *              object indicating what is wrong with the classpath or output location
+        */
+       public static IJavaModelStatus validateClasspath(IJavaProject javaProject, IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {
+       
+               IProject project = javaProject.getProject();
+               IPath projectPath= project.getFullPath();
+               String projectName = javaProject.getElementName();
+       
+               /* validate output location */
+               if (projectOutputLocation == null) {
+                       return new JavaModelStatus(IJavaModelStatusConstants.NULL_PATH);
+               }
+               if (projectOutputLocation.isAbsolute()) {
+                       if (!projectPath.isPrefixOf(projectOutputLocation)) {
+                               return new JavaModelStatus(IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT, javaProject, projectOutputLocation.toString());
+                       }
+               } else {
+                       return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, projectOutputLocation);
+               }
+       
+               boolean hasSource = false;
+               boolean hasLibFolder = false;
+       
+
+               // tolerate null path, it will be reset to default
+               if (rawClasspath == null) 
+                       return JavaModelStatus.VERIFIED_OK;
+               
+               // retrieve resolved classpath
+               IClasspathEntry[] classpath; 
+               try {
+                       classpath = ((JavaProject)javaProject).getResolvedClasspath(rawClasspath, null /*output*/, true/*ignore pb*/, false/*no marker*/, null /*no reverse map*/);
+               } catch(JavaModelException e){
+                       return e.getJavaModelStatus();
+               }
+               int length = classpath.length; 
+
+               int outputCount = 1;
+               IPath[] outputLocations = new IPath[length+1];
+               boolean[] allowNestingInOutputLocations = new boolean[length+1];
+               outputLocations[0] = projectOutputLocation;
+               
+               // retrieve and check output locations
+               IPath potentialNestedOutput = null; // for error reporting purpose
+               int sourceEntryCount = 0;
+               boolean disableExclusionPatterns = JavaCore.DISABLED.equals(javaProject.getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, true));
+               boolean disableCustomOutputLocations = JavaCore.DISABLED.equals(javaProject.getOption(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, true));
+               
+               for (int i = 0 ; i < length; i++) {
+                       IClasspathEntry resolvedEntry = classpath[i];
+                       switch(resolvedEntry.getEntryKind()){
+                               case IClasspathEntry.CPE_SOURCE :
+                                       sourceEntryCount++;
+
+                                       if (disableExclusionPatterns &&
+                                               ((resolvedEntry.getInclusionPatterns() != null && resolvedEntry.getInclusionPatterns().length > 0) 
+                                               || (resolvedEntry.getExclusionPatterns() != null && resolvedEntry.getExclusionPatterns().length > 0))) {
+                                               return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS, javaProject, resolvedEntry.getPath());
+                                       }
+                                       IPath customOutput; 
+                                       if ((customOutput = resolvedEntry.getOutputLocation()) != null) {
+
+                                               if (disableCustomOutputLocations) {
+                                                       return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS, javaProject, resolvedEntry.getPath());
+                                               }
+                                               // ensure custom output is in project
+                                               if (customOutput.isAbsolute()) {
+                                                       if (!javaProject.getPath().isPrefixOf(customOutput)) {
+                                                               return new JavaModelStatus(IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT, javaProject, customOutput.toString());
+                                                       }
+                                               } else {
+                                                       return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, customOutput);
+                                               }
+                                               
+                                               // ensure custom output doesn't conflict with other outputs
+                                               // check exact match
+                                               if (Util.indexOfMatchingPath(customOutput, outputLocations, outputCount) != -1) {
+                                                       continue; // already found
+                                               }
+                                               // accumulate all outputs, will check nesting once all available (to handle ordering issues)
+                                               outputLocations[outputCount++] = customOutput;
+                                       }
+                       }
+               }
+               // check nesting across output locations
+               for (int i = 1 /*no check for default output*/ ; i < outputCount; i++) {
+                   IPath customOutput = outputLocations[i];
+                   int index;
+                       // check nesting
+                       if ((index = Util.indexOfEnclosingPath(customOutput, outputLocations, outputCount)) != -1 && index != i) {
+                               if (index == 0) {
+                                       // custom output is nested in project's output: need to check if all source entries have a custom
+                                       // output before complaining
+                                       if (potentialNestedOutput == null) potentialNestedOutput = customOutput;
+                               } else {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotNestOutputInOutput", customOutput.makeRelative().toString(), outputLocations[index].makeRelative().toString())); //$NON-NLS-1$
+                               }
+                       }
+               }       
+               // allow custom output nesting in project's output if all source entries have a custom output
+               if (sourceEntryCount <= outputCount-1) {
+                   allowNestingInOutputLocations[0] = true;
+               } else if (potentialNestedOutput != null) {
+                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotNestOutputInOutput", potentialNestedOutput.makeRelative().toString(), outputLocations[0].makeRelative().toString())); //$NON-NLS-1$
+               }
+
+               for (int i = 0 ; i < length; i++) {
+                       IClasspathEntry resolvedEntry = classpath[i];
+                       IPath path = resolvedEntry.getPath();
+                       int index;
+                       switch(resolvedEntry.getEntryKind()){
+                               
+                               case IClasspathEntry.CPE_SOURCE :
+                                       hasSource = true;
+                                       if ((index = Util.indexOfMatchingPath(path, outputLocations, outputCount)) != -1){
+                                               allowNestingInOutputLocations[index] = true;
+                                       }
+                                       break;
+
+//                             case IClasspathEntry.CPE_LIBRARY:
+//                                     hasLibFolder |= !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(path.lastSegment());
+//                                     if ((index = Util.indexOfMatchingPath(path, outputLocations, outputCount)) != -1){
+//                                             allowNestingInOutputLocations[index] = true;
+//                                     }
+//                                     break;
+                       }
+               }
+               if (!hasSource && !hasLibFolder) { // if no source and no lib folder, then allowed
+                       for (int i = 0; i < outputCount; i++) allowNestingInOutputLocations[i] = true;
+               }
+               
+               HashSet pathes = new HashSet(length);
+               
+               // check all entries
+               for (int i = 0 ; i < length; i++) {
+                       IClasspathEntry entry = classpath[i];
+                       if (entry == null) continue;
+                       IPath entryPath = entry.getPath();
+                       int kind = entry.getEntryKind();
+                       
+                       // Build some common strings for status message
+                       boolean isProjectRelative = entryPath.segment(0).toString().equals(projectName);
+                       String entryPathMsg = isProjectRelative ? entryPath.removeFirstSegments(1).toString() : entryPath.makeRelative().toString();
+       
+                       // complain if duplicate path
+                       if (!pathes.add(entryPath)){
+                               return new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Util.bind("classpath.duplicateEntryPath", entryPathMsg, projectName)); //$NON-NLS-1$
+                       }
+                       // no further check if entry coincidates with project or output location
+                       if (entryPath.equals(projectPath)){
+                               // complain if self-referring project entry
+                               if (kind == IClasspathEntry.CPE_PROJECT){
+                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, Util.bind("classpath.cannotReferToItself", entryPath.makeRelative().toString()));//$NON-NLS-1$
+                               }
+                               // tolerate nesting output in src if src==prj
+                               continue;
+                       }
+       
+                       // allow nesting source entries in each other as long as the outer entry excludes the inner one
+                       if (kind == IClasspathEntry.CPE_SOURCE ) {
+//                                     || (kind == IClasspathEntry.CPE_LIBRARY && !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(entryPath.lastSegment()))){
+                               for (int j = 0; j < classpath.length; j++){
+                                       IClasspathEntry otherEntry = classpath[j];
+                                       if (otherEntry == null) continue;
+                                       int otherKind = otherEntry.getEntryKind();
+                                       IPath otherPath = otherEntry.getPath();
+                                       if (entry != otherEntry 
+                                               && (otherKind == IClasspathEntry.CPE_SOURCE ) ) {
+//                                                             || (otherKind == IClasspathEntry.CPE_LIBRARY 
+//                                                                             && !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(otherPath.lastSegment())))){
+                                               char[][] inclusionPatterns, exclusionPatterns;
+                                               if (otherPath.isPrefixOf(entryPath) 
+                                                               && !otherPath.equals(entryPath)
+                                                               && !Util.isExcluded(entryPath.append("*"), inclusionPatterns = ((ClasspathEntry)otherEntry).fullInclusionPatternChars(), exclusionPatterns = ((ClasspathEntry)otherEntry).fullExclusionPatternChars(), false)) { //$NON-NLS-1$
+                                                       String exclusionPattern = entryPath.removeFirstSegments(otherPath.segmentCount()).segment(0);
+                                                       if (Util.isExcluded(entryPath, inclusionPatterns, exclusionPatterns, false)) {
+                                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.mustEndWithSlash", exclusionPattern, entryPath.makeRelative().toString())); //$NON-NLS-1$
+                                                       } else {
+                                                               if (otherKind == IClasspathEntry.CPE_SOURCE) {
+                                                                       exclusionPattern += '/';
+                                                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotNestEntryInEntry", new String[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString(), exclusionPattern})); //$NON-NLS-1$
+                                                               } else {
+                                                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotNestEntryInLibrary", entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString())); //$NON-NLS-1$
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       // prevent nesting output location inside entry unless enclosing is a source entry which explicitly exclude the output location
+                   char[][] inclusionPatterns = ((ClasspathEntry)entry).fullInclusionPatternChars();
+                   char[][] exclusionPatterns = ((ClasspathEntry)entry).fullExclusionPatternChars();
+                   for (int j = 0; j < outputCount; j++){
+                       IPath currentOutput = outputLocations[j];
+                       if (entryPath.equals(currentOutput)) continue;
+                               if (entryPath.isPrefixOf(currentOutput)) {
+                                   if (kind != IClasspathEntry.CPE_SOURCE || !Util.isExcluded(currentOutput, inclusionPatterns, exclusionPatterns, true)) {
+                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotNestOutputInEntry", currentOutput.makeRelative().toString(), entryPath.makeRelative().toString())); //$NON-NLS-1$
+                                   }
+                               }
+                   }
+
+                   // prevent nesting entry inside output location - when distinct from project or a source folder
+                   for (int j = 0; j < outputCount; j++){
+                       if (allowNestingInOutputLocations[j]) continue;
+                       IPath currentOutput = outputLocations[j];
+                               if (currentOutput.isPrefixOf(entryPath)) {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotNestEntryInOutput", entryPath.makeRelative().toString(), currentOutput.makeRelative().toString())); //$NON-NLS-1$
+                               }
+                   }                   
+               }
+               // ensure that no specific output is coincidating with another source folder (only allowed if matching current source folder)
+               // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
+               // perform one separate iteration so as to not take precedence over previously checked scenarii (in particular should
+               // diagnose nesting source folder issue before this one, for example, [src]"Project/", [src]"Project/source/" and output="Project/" should
+               // first complain about missing exclusion pattern
+               for (int i = 0 ; i < length; i++) {
+                       IClasspathEntry entry = classpath[i];
+                       if (entry == null) continue;
+                       IPath entryPath = entry.getPath();
+                       int kind = entry.getEntryKind();
+
+                       // Build some common strings for status message
+                       boolean isProjectRelative = entryPath.segment(0).toString().equals(projectName);
+                       String entryPathMsg = isProjectRelative ? entryPath.removeFirstSegments(1).toString() : entryPath.makeRelative().toString();
+       
+                       if (kind == IClasspathEntry.CPE_SOURCE) {
+                               IPath output = entry.getOutputLocation();
+                               if (output == null) continue; // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
+                               // if (output == null) output = projectOutputLocation; // if no specific output, still need to check using default output (this line would check default output)
+                               for (int j = 0; j < length; j++) {
+                                       IClasspathEntry otherEntry = classpath[j];
+                                       if (otherEntry == entry) continue;
+
+                                       // Build some common strings for status message
+                                       boolean opStartsWithProject = otherEntry.getPath().segment(0).toString().equals(projectName);
+                                       String otherPathMsg = opStartsWithProject ? otherEntry.getPath().removeFirstSegments(1).toString() : otherEntry.getPath().makeRelative().toString();
+       
+                                       switch (otherEntry.getEntryKind()) {
+                                               case IClasspathEntry.CPE_SOURCE :
+                                                       if (otherEntry.getPath().equals(output)) {
+                                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotUseDistinctSourceFolderAsOutput", new String[] {entryPathMsg, otherPathMsg, projectName})); //$NON-NLS-1$
+                                                       }
+                                                       break;
+                                               case IClasspathEntry.CPE_LIBRARY :
+                                                       if (otherEntry.getPath().equals(output)) {
+                                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.cannotUseLibraryAsOutput", new String[] {entryPathMsg, otherPathMsg, projectName})); //$NON-NLS-1$
+                                                       }
+                                       }
+                               }
+                       }                       
+               }
+               return JavaModelStatus.VERIFIED_OK;     
+       }
+       
+       /**
+        * Returns a Java model status describing the problem related to this classpath entry if any, 
+        * a status object with code <code>IStatus.OK</code> if the entry is fine (that is, if the
+        * given classpath entry denotes a valid element to be referenced onto a classpath).
+        * 
+        * @param project the given java project
+        * @param entry the given classpath entry
+        * @param checkSourceAttachment a flag to determine if source attachement should be checked
+        * @param recurseInContainers flag indicating whether validation should be applied to container entries recursively
+        * @return a java model status describing the problem related to this classpath entry if any, a status object with code <code>IStatus.OK</code> if the entry is fine
+        */
+       public static IJavaModelStatus validateClasspathEntry(IJavaProject project, IClasspathEntry entry, boolean checkSourceAttachment, boolean recurseInContainers){
+               
+               IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();                        
+               IPath path = entry.getPath();
+       
+               // Build some common strings for status message
+               String projectName = project.getElementName();
+               boolean pathStartsWithProject = path.segment(0).toString().equals(projectName);
+               String entryPathMsg = pathStartsWithProject ? path.removeFirstSegments(1).toString() : path.makeRelative().toString();
+       
+               switch(entry.getEntryKind()){
+       
+                       // container entry check
+//                     case IClasspathEntry.CPE_CONTAINER :
+//                             if (path != null && path.segmentCount() >= 1){
+//                                     try {
+//                                             IClasspathContainer container = JavaModelManager.getJavaModelManager().getClasspathContainer(path, project);
+//                                             // container retrieval is performing validation check on container entry kinds.
+//                                             if (container == null){
+//                                                     return new JavaModelStatus(IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND, project, path);
+//                                             } else if (container == JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
+//                                                     // don't create a marker if initialization is in progress (case of cp initialization batching)
+//                                                     return JavaModelStatus.VERIFIED_OK;
+//                                             }
+//                                             IClasspathEntry[] containerEntries = container.getClasspathEntries();
+//                                             if (containerEntries != null){
+//                                                     for (int i = 0, length = containerEntries.length; i < length; i++){
+//                                                             IClasspathEntry containerEntry = containerEntries[i];
+//                                                             int kind = containerEntry == null ? 0 : containerEntry.getEntryKind();
+//                                                             if (containerEntry == null
+//                                                                     || kind == IClasspathEntry.CPE_SOURCE
+//                                                                     || kind == IClasspathEntry.CPE_VARIABLE
+//                                                                     || kind == IClasspathEntry.CPE_CONTAINER){
+//                                                                             String description = container.getDescription();
+//                                                                             if (description == null) description = path.makeRelative().toString();
+//                                                                             return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CP_CONTAINER_ENTRY, project, path);
+//                                                             }
+//                                                             if (recurseInContainers) {
+//                                                                     IJavaModelStatus containerEntryStatus = validateClasspathEntry(project, containerEntry, checkSourceAttachment, recurseInContainers);
+//                                                                     if (!containerEntryStatus.isOK()){
+//                                                                             return containerEntryStatus;
+//                                                                     }
+//                                                             } 
+//                                                     }
+//                                             }
+//                                     } catch(JavaModelException e){
+//                                             return new JavaModelStatus(e);
+//                                     }
+//                             } else {
+//                                     return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalContainerPath", entryPathMsg, projectName));                                         //$NON-NLS-1$
+//                             }
+//                             break;
+                               
+                       // variable entry check
+                       case IClasspathEntry.CPE_VARIABLE :
+                               if (path != null && path.segmentCount() >= 1){
+                                       try {
+                                               entry = JavaCore.getResolvedClasspathEntry(entry);
+                                       } catch (Assert.AssertionFailedException e) {
+                                               // Catch the assertion failure and throw java model exception instead
+                                               // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
+                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
+                                       }
+                                       if (entry == null){
+                                               return new JavaModelStatus(IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND, project, path);
+                                       }
+                                       return validateClasspathEntry(project, entry, checkSourceAttachment, recurseInContainers);
+                               } else {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalVariablePath", path.makeRelative().toString(), projectName));                                        //$NON-NLS-1$
+                               }
+       
+                       // library entry check
+//                     case IClasspathEntry.CPE_LIBRARY :
+//                             if (path != null && path.isAbsolute() && !path.isEmpty()) {
+//                                     IPath sourceAttachment = entry.getSourceAttachmentPath();
+//                                     Object target = JavaModel.getTarget(workspaceRoot, path, true);
+//                                     if (target != null && project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true) != JavaCore.IGNORE) {
+//                                             long projectTargetJDK = CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
+//                                             long libraryJDK = Util.getJdkLevel(target);
+//                                             if (libraryJDK != 0 && libraryJDK > projectTargetJDK) {
+//                                                     return new JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL, project, path, CompilerOptions.versionFromJdkLevel(libraryJDK)); 
+//                                             }
+//                                     }
+//                                     if (target instanceof IResource){
+//                                             IResource resolvedResource = (IResource) target;
+//                                             switch(resolvedResource.getType()){
+//                                                     case IResource.FILE :
+//                                                             if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(resolvedResource.getName())) {
+//                                                                     if (checkSourceAttachment 
+//                                                                             && sourceAttachment != null
+//                                                                             && !sourceAttachment.isEmpty()
+//                                                                             && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
+//                                                                             return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceAttachment", new String [] {sourceAttachment.makeRelative().toString(), path.makeRelative().toString(), projectName})); //$NON-NLS-1$
+//                                                                     }
+//                                                             } else {
+//                                                                     return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalLibraryArchive", entryPathMsg, projectName)); //$NON-NLS-1$
+//                                                             }
+//                                                             break;
+//                                                     case IResource.FOLDER : // internal binary folder
+//                                                             if (checkSourceAttachment 
+//                                                                     && sourceAttachment != null 
+//                                                                     && !sourceAttachment.isEmpty()
+//                                                                     && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
+//                                                                     return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceAttachment", new String [] {sourceAttachment.makeRelative().toString(), path.makeRelative().toString(), projectName})); //$NON-NLS-1$
+//                                                             }
+//                                             }
+//                                     } else if (target instanceof File){
+//                                         File file = (File) target;
+//                                         if (!file.isFile()) {
+//                                                     return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalExternalFolder", path.toOSString(), projectName)); //$NON-NLS-1$
+//                                         } else if (!org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())) {
+//                                                     return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalLibraryArchive", path.toOSString(), projectName)); //$NON-NLS-1$
+//                                         } else if (checkSourceAttachment 
+//                                                             && sourceAttachment != null 
+//                                                             && !sourceAttachment.isEmpty()
+//                                                             && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
+//                                                             return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceAttachment", new String [] {sourceAttachment.toString(), path.makeRelative().toString(), projectName})); //$NON-NLS-1$
+//                                         }
+//                                     } else {
+//                                             return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundLibrary", path.makeRelative().toString(), projectName)); //$NON-NLS-1$
+//                                     }
+//                             } else {
+//                                     return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalLibraryPath", path.makeRelative().toString(), projectName)); //$NON-NLS-1$
+//                             }
+//                             break;
+       
+                       // project entry check
+                       case IClasspathEntry.CPE_PROJECT :
+                               if (path != null && path.isAbsolute() && !path.isEmpty()) {
+                                       IProject prereqProjectRsc = workspaceRoot.getProject(path.segment(0));
+                                       IJavaProject prereqProject = JavaCore.create(prereqProjectRsc);
+                                       try {
+                                               if (!prereqProjectRsc.exists() || !prereqProjectRsc.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)){
+                                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundProject", path.makeRelative().segment(0).toString(), projectName)); //$NON-NLS-1$
+                                               }
+                                               if (!prereqProjectRsc.isOpen()){
+                                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.closedProject", path.segment(0).toString())); //$NON-NLS-1$
+                                               }
+//                                             if (project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true) != JavaCore.IGNORE) {
+//                                                     long projectTargetJDK = CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
+//                                                     long prereqProjectTargetJDK = CompilerOptions.versionToJdkLevel(prereqProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
+//                                                     if (prereqProjectTargetJDK > projectTargetJDK) {
+//                                                             return new JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL, project, path, CompilerOptions.versionFromJdkLevel(prereqProjectTargetJDK)); 
+//                                                     }
+//                                             }
+                                       } catch (CoreException e){
+                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundProject", path.segment(0).toString(), projectName)); //$NON-NLS-1$
+                                       }
+                               } else {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalProjectPath", path.segment(0).toString(), projectName)); //$NON-NLS-1$
+                               }
+                               break;
+       
+                       // project source folder
+                       case IClasspathEntry.CPE_SOURCE :
+                               if (((entry.getInclusionPatterns() != null && entry.getInclusionPatterns().length > 0)
+                                               || (entry.getExclusionPatterns() != null && entry.getExclusionPatterns().length > 0))
+                                               && JavaCore.DISABLED.equals(project.getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, true))) {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS, project, path);
+                               }
+                               if (entry.getOutputLocation() != null && JavaCore.DISABLED.equals(project.getOption(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, true))) {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS, project, path);
+                               }
+                               if (path != null && path.isAbsolute() && !path.isEmpty()) {
+                                       IPath projectPath= project.getProject().getFullPath();
+                                       if (!projectPath.isPrefixOf(path) || JavaModel.getTarget(workspaceRoot, path, true) == null){
+                                               return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.unboundSourceFolder", entryPathMsg, projectName)); //$NON-NLS-1$
+                                       }
+                               } else {
+                                       return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Util.bind("classpath.illegalSourceFolderPath", entryPathMsg, projectName)); //$NON-NLS-1$
+                               }
+                               break;
+               }
+               return JavaModelStatus.VERIFIED_OK;             
+       }
 }
+