X-Git-Url: http://secure.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/ClasspathEntry.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/ClasspathEntry.java index bdb0401..6ca5c30 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/ClasspathEntry.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/ClasspathEntry.java @@ -10,14 +10,28 @@ *******************************************************************************/ 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.PHPCore; +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; @@ -63,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 */ @@ -122,6 +146,7 @@ public class ClasspathEntry implements IClasspathEntry { int contentKind, int entryKind, IPath path, + IPath[] inclusionPatterns, IPath[] exclusionPatterns, IPath sourceAttachmentPath, IPath sourceAttachmentRootPath, @@ -131,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; @@ -149,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(); @@ -158,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. */ @@ -256,7 +303,7 @@ public class ClasspathEntry implements IClasspathEntry { switch (kind) { case IClasspathEntry.CPE_PROJECT : - return PHPCore.newProjectEntry(path, isExported); + return JavaCore.newProjectEntry(path, isExported); // case IClasspathEntry.CPE_LIBRARY : // return JavaCore.newLibraryEntry( @@ -269,9 +316,9 @@ public class ClasspathEntry implements IClasspathEntry { // must be an entry in this project or specify another project String projSegment = path.segment(0); if (projSegment != null && projSegment.equals(project.getElementName())) { // this project - return PHPCore.newSourceEntry(path, exclusionPatterns, outputLocation); + return JavaCore.newSourceEntry(path, exclusionPatterns, outputLocation); } else { // another project - return PHPCore.newProjectEntry(path, isExported); + return JavaCore.newProjectEntry(path, isExported); } // case IClasspathEntry.CPE_VARIABLE : @@ -282,21 +329,23 @@ public class ClasspathEntry implements IClasspathEntry { // isExported); case IClasspathEntry.CPE_CONTAINER : - return PHPCore.newContainerEntry( + return JavaCore.newContainerEntry( path, 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$ } @@ -342,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; @@ -389,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() */ @@ -582,6 +650,544 @@ public class ClasspathEntry implements IClasspathEntry { */ public IClasspathEntry getResolvedEntry() { - return PHPCore.getResolvedClasspathEntry(this); + return JavaCore.getResolvedClasspathEntry(this); + } + /** + * Returns the XML encoding of the class path. + */ + public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine) { + HashMap parameters = new HashMap(); + + parameters.put("kind", ClasspathEntry.kindToString(this.entryKind));//$NON-NLS-1$ + + IPath xmlPath = this.path; + if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) { + // translate to project relative from absolute (unless a device path) + if (xmlPath.isAbsolute()) { + if (projectPath != null && projectPath.isPrefixOf(xmlPath)) { + if (xmlPath.segment(0).equals(projectPath.segment(0))) { + xmlPath = xmlPath.removeFirstSegments(1); + xmlPath = xmlPath.makeRelative(); + } else { + xmlPath = xmlPath.makeAbsolute(); + } + } + } + } + parameters.put("path", String.valueOf(xmlPath));//$NON-NLS-1$ + + if (this.sourceAttachmentPath != null) { + xmlPath = this.sourceAttachmentPath; + // translate to project relative from absolute + if (this.entryKind != IClasspathEntry.CPE_VARIABLE && projectPath != null && projectPath.isPrefixOf(xmlPath)) { + if (xmlPath.segment(0).equals(projectPath.segment(0))) { + xmlPath = xmlPath.removeFirstSegments(1); + xmlPath = xmlPath.makeRelative(); + } + } + parameters.put("sourcepath", String.valueOf(xmlPath));//$NON-NLS-1$ + } + if (this.sourceAttachmentRootPath != null) { + parameters.put("rootpath", String.valueOf(this.sourceAttachmentRootPath));//$NON-NLS-1$ + } + if (this.isExported) { + parameters.put("exported", "true");//$NON-NLS-1$//$NON-NLS-2$ + } +// if (this.inclusionPatterns != null && this.inclusionPatterns.length > 0) { +// StringBuffer includeRule = new StringBuffer(10); +// for (int i = 0, max = this.inclusionPatterns.length; i < max; i++){ +// if (i > 0) includeRule.append('|'); +// includeRule.append(this.inclusionPatterns[i]); +// } +// parameters.put("including", String.valueOf(includeRule));//$NON-NLS-1$ +// } + if (this.exclusionPatterns != null && this.exclusionPatterns.length > 0) { + StringBuffer excludeRule = new StringBuffer(10); + for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){ + if (i > 0) excludeRule.append('|'); + excludeRule.append(this.exclusionPatterns[i]); + } + parameters.put("excluding", String.valueOf(excludeRule));//$NON-NLS-1$ + } + + if (this.specificOutputLocation != null) { + IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1); + outputLocation = outputLocation.makeRelative(); + parameters.put("output", String.valueOf(outputLocation));//$NON-NLS-1$ + } + + writer.printTag("classpathentry", parameters, indent, newLine, true);//$NON-NLS-1$ + } + + /** + * Validate a given classpath and output location for a project, using the following rules: + * + * + * 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, ...). + *

+ * 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). + *

+ * @param javaProject the given java project + * @param rawClasspath a given classpath + * @param projectOutputLocation a given output location + * @return a status object with code IStatus.OK 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 IStatus.OK 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 IStatus.OK 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; } } +