### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/util/Util.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java,v retrieving revision 1.70 diff -u -r1.70 Util.java --- src/org/eclipse/jdt/core/tests/util/Util.java 22 Sep 2008 15:04:25 -0000 1.70 +++ src/org/eclipse/jdt/core/tests/util/Util.java 7 Oct 2008 13:28:51 -0000 @@ -349,10 +349,12 @@ if (pathsAndContents != null) { compile(pathsAndContents, options, classpath, classesPath); } - for (int i = 0, l = extraPathsAndContents == null ? 0 : extraPathsAndContents.length; i < l; /* inc in loop */) { - File outputFile = new File(classesPath, extraPathsAndContents[i++]); - outputFile.getParentFile().mkdirs(); - Util.writeToFile(extraPathsAndContents[i++], outputFile.getAbsolutePath()); + if (extraPathsAndContents != null) { + for (int i = 0, l = extraPathsAndContents == null ? 0 : extraPathsAndContents.length; i < l; /* inc in loop */) { + File outputFile = new File(classesPath, extraPathsAndContents[i++]); + outputFile.getParentFile().mkdirs(); + Util.writeToFile(extraPathsAndContents[i++], outputFile.getAbsolutePath()); + } } zip(classesDir, jarPath); } #P org.eclipse.jdt.core Index: batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java,v retrieving revision 1.46 diff -u -r1.46 ClasspathJar.java --- batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java 27 Jun 2008 16:04:12 -0000 1.46 +++ batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java 7 Oct 2008 13:28:52 -0000 @@ -14,7 +14,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.io.Reader; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; @@ -28,6 +27,7 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.util.ManifestAnalyzer; import org.eclipse.jdt.internal.compiler.util.Util; public class ClasspathJar extends ClasspathLocation { @@ -44,120 +44,6 @@ this.closeZipFileAtEnd = closeZipFileAtEnd; } -// manifest file analyzer limited to Class-Path sections analysis -public static class ManifestAnalyzer { - private static final int - START = 0, - IN_CLASSPATH_HEADER = 1, // multistate - PAST_CLASSPATH_HEADER = 2, - SKIPPING_WHITESPACE = 3, - READING_JAR = 4, - CONTINUING = 5, - SKIP_LINE = 6; - private static final char[] CLASSPATH_HEADER_TOKEN = - "Class-Path:".toCharArray(); //$NON-NLS-1$ - private int ClasspathSectionsCount; - private ArrayList calledFilesNames; - public boolean analyzeManifestContents(Reader reader) throws IOException { - int state = START, substate = 0; - StringBuffer currentJarToken = new StringBuffer(); - int currentChar; - this.ClasspathSectionsCount = 0; - this.calledFilesNames = null; - for (;;) { - currentChar = reader.read(); - switch (state) { - case START: - if (currentChar == -1) { - return true; - } else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) { - state = IN_CLASSPATH_HEADER; - substate = 1; - } else { - state = SKIP_LINE; - } - break; - case IN_CLASSPATH_HEADER: - if (currentChar == -1) { - return true; - } else if (currentChar == '\n') { - state = START; - } else if (currentChar != CLASSPATH_HEADER_TOKEN[substate++]) { - state = SKIP_LINE; - } else if (substate == CLASSPATH_HEADER_TOKEN.length) { - state = PAST_CLASSPATH_HEADER; - } - break; - case PAST_CLASSPATH_HEADER: - if (currentChar == ' ') { - state = SKIPPING_WHITESPACE; - this.ClasspathSectionsCount++; - } else { - return false; - } - break; - case SKIPPING_WHITESPACE: - if (currentChar == -1) { - return true; - } else if (currentChar == '\n') { - state = CONTINUING; - } else if (currentChar != ' ') { - currentJarToken.append((char) currentChar); - state = READING_JAR; - } - break; - case CONTINUING: - if (currentChar == -1) { - return true; - } else if (currentChar == '\n') { - state = START; - } else if (currentChar == ' ') { - state = SKIPPING_WHITESPACE; - } else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) { - state = IN_CLASSPATH_HEADER; - substate = 1; - } else if (this.calledFilesNames == null) { - return false; - } else { - state = SKIP_LINE; - } - break; - case SKIP_LINE: - if (currentChar == -1) { - return true; - } else if (currentChar == '\n') { - state = START; - } - break; - case READING_JAR: - if (currentChar == -1) { - return false; - } else if (currentChar == '\n') { - // appends token below - state = CONTINUING; - } else if (currentChar == ' ') { - // appends token below - state = SKIPPING_WHITESPACE; - } else { - currentJarToken.append((char) currentChar); - break; - } - if (this.calledFilesNames == null) { - this.calledFilesNames = new ArrayList(); - } - this.calledFilesNames.add(currentJarToken.toString()); - currentJarToken.setLength(0); - break; - } - } - } - public int getClasspathSectionsCount() { - return this.ClasspathSectionsCount; - } - public List getCalledFileNames() { - return this.calledFilesNames; - } -} public static final ManifestAnalyzer MANIFEST_ANALYZER = new ManifestAnalyzer(); public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) { Index: model/org/eclipse/jdt/internal/core/JavaProject.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java,v retrieving revision 1.416 diff -u -r1.416 JavaProject.java --- model/org/eclipse/jdt/internal/core/JavaProject.java 1 Oct 2008 16:33:13 -0000 1.416 +++ model/org/eclipse/jdt/internal/core/JavaProject.java 7 Oct 2008 13:28:55 -0000 @@ -2469,6 +2469,15 @@ } } + public ClasspathChange resetResolvedClasspath() { + try { + return getPerProjectInfo().resetResolvedClasspath(); + } catch (JavaModelException e) { + // project doesn't exist + return null; + } + } + /* * Resolve the given raw classpath. */ @@ -2494,13 +2503,12 @@ IClasspathEntry rawEntry = rawClasspath[i]; IClasspathEntry resolvedEntry = rawEntry; - IPath resolvedPath; switch (rawEntry.getEntryKind()){ case IClasspathEntry.CPE_VARIABLE : try { - resolvedEntry = manager.getResolvedClasspathEntry(rawEntry, usePreviousSession); + resolvedEntry = manager.resolveVariableEntry(rawEntry, usePreviousSession); } catch (ClasspathEntry.AssertionFailedException e) { // Catch the assertion failure and set status instead // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992 @@ -2510,14 +2518,14 @@ if (resolvedEntry == null) { result.unresolvedEntryStatus = new JavaModelStatus(IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND, this, rawEntry.getPath()); } else { - if (result.rawReverseMap.get(resolvedPath = resolvedEntry.getPath()) == null) { - result.rawReverseMap.put(resolvedPath , rawEntry); - result.rootPathToResolvedEntries.put(resolvedPath, resolvedEntry); - } - resolvedEntries.add(resolvedEntry); - if (resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && ExternalFoldersManager.isExternalFolderPath(resolvedPath)) { - externalFoldersManager.addFolder(resolvedPath); // no-op if not an external folder or if already registered + if (resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { + // resolve Class-Path: in manifest + ClasspathEntry[] extraEntries = ((ClasspathEntry) resolvedEntry).resolvedChainedLibraries(); + for (int j = 0, length2 = extraEntries.length; j < length2; j++) { + addToResult(rawEntry, extraEntries[j], result, resolvedEntries, externalFoldersManager); + } } + addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager); } break; @@ -2547,34 +2555,36 @@ } // if container is exported or restricted, then its nested entries must in turn be exported (21749) and/or propagate restrictions cEntry = cEntry.combineWith((ClasspathEntry) rawEntry); - // resolve ".." in library path + if (cEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { + // resolve ".." in library path cEntry = cEntry.resolvedDotDot(); + + // resolve Class-Path: in manifest + ClasspathEntry[] extraEntries = cEntry.resolvedChainedLibraries(); + for (int k = 0, length2 = extraEntries.length; k < length2; k++) { + addToResult(rawEntry, extraEntries[k], result, resolvedEntries, externalFoldersManager); + } } - if (result.rawReverseMap.get(resolvedPath = cEntry.getPath()) == null) { - result.rawReverseMap.put(resolvedPath , rawEntry); - result.rootPathToResolvedEntries.put(resolvedPath, cEntry); - } - resolvedEntries.add(cEntry); - if (cEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && ExternalFoldersManager.isExternalFolderPath(resolvedPath)) { - externalFoldersManager.addFolder(resolvedPath); // no-op if not an external folder or if already registered - } + addToResult(rawEntry, cEntry, result, resolvedEntries, externalFoldersManager); } break; case IClasspathEntry.CPE_LIBRARY: + // resolve ".." in library path resolvedEntry = ((ClasspathEntry) rawEntry).resolvedDotDot(); - // $FALL-THROUGH$ use the default code below - default : - if (result.rawReverseMap.get(resolvedPath = resolvedEntry.getPath()) == null) { - result.rawReverseMap.put(resolvedPath , rawEntry); - result.rootPathToResolvedEntries.put(resolvedPath, resolvedEntry); - } - resolvedEntries.add(resolvedEntry); - if (resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && ExternalFoldersManager.isExternalFolderPath(resolvedPath)) { - externalFoldersManager.addFolder(resolvedPath); // no-op if not an external folder or if already registered + + // resolve Class-Path: in manifest + ClasspathEntry[] extraEntries = ((ClasspathEntry) resolvedEntry).resolvedChainedLibraries(); + for (int k = 0, length2 = extraEntries.length; k < length2; k++) { + addToResult(rawEntry, extraEntries[k], result, resolvedEntries, externalFoldersManager); } + addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager); + break; + default : + addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager); + break; } } result.resolvedClasspath = new IClasspathEntry[resolvedEntries.size()]; @@ -2582,6 +2592,18 @@ return result; } + private void addToResult(IClasspathEntry rawEntry, IClasspathEntry resolvedEntry, ResolvedClasspath result, ArrayList resolvedEntries, ExternalFoldersManager externalFoldersManager) { + IPath resolvedPath; + if (result.rawReverseMap.get(resolvedPath = resolvedEntry.getPath()) == null) { + result.rawReverseMap.put(resolvedPath, rawEntry); + result.rootPathToResolvedEntries.put(resolvedPath, resolvedEntry); + } + resolvedEntries.add(resolvedEntry); + if (resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && ExternalFoldersManager.isExternalFolderPath(resolvedPath)) { + externalFoldersManager.addFolder(resolvedPath); // no-op if not an external folder or if already registered + } + } + /* * Resolve the given perProjectInfo's raw classpath and store the resolved classpath in the perProjectInfo. */ Index: model/org/eclipse/jdt/internal/core/ClasspathEntry.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java,v retrieving revision 1.104 diff -u -r1.104 ClasspathEntry.java --- model/org/eclipse/jdt/internal/core/ClasspathEntry.java 26 Sep 2008 09:01:23 -0000 1.104 +++ model/org/eclipse/jdt/internal/core/ClasspathEntry.java 7 Oct 2008 13:28:54 -0000 @@ -10,16 +10,24 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; @@ -44,6 +52,7 @@ import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.env.AccessRule; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.util.ManifestAnalyzer; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; import org.w3c.dom.DOMException; @@ -133,6 +142,8 @@ private IPath[] exclusionPatterns; private char[][] fullExclusionPatternChars; private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$ + private final static ClasspathEntry[] NO_ENTRIES = new ClasspathEntry[0]; + private final static IPath[] NO_PATHS = new IPath[0]; private boolean combineAccessRules; @@ -848,6 +859,74 @@ } /* + * Read the Class-Path clause of the manifest of the jar pointed by this path, and return + * the corresponding paths. + */ + public static IPath[] resolvedChainedLibraries(IPath jarPath) { + ArrayList result = new ArrayList(); + resolvedChainedLibraries(jarPath, new HashSet(), result); + if (result.size() == 0) + return NO_PATHS; + return (IPath[]) result.toArray(new IPath[result.size()]); + } + + private static void resolvedChainedLibraries(IPath jarPath, HashSet visited, ArrayList result) { + if (visited.contains( jarPath)) + return; + visited.add(jarPath); + Object target = JavaModel.getTarget(jarPath, true/*check existence, otherwise the manifest cannot be read*/); + if (target instanceof IFile || target instanceof File) { + JavaModelManager manager = JavaModelManager.getJavaModelManager(); + ZipFile zip = null; + BufferedReader reader = null; + try { + zip = manager.getZipFile(jarPath); + ZipEntry manifest = zip.getEntry("META-INF/MANIFEST.MF"); //$NON-NLS-1$ + if (manifest != null) { // non-null implies regular file + reader = new BufferedReader(new InputStreamReader(zip.getInputStream(manifest))); + ManifestAnalyzer analyzer = new ManifestAnalyzer(); + boolean success = analyzer.analyzeManifestContents(reader); + List calledFileNames = analyzer.getCalledFileNames(); + if (!success || analyzer.getClasspathSectionsCount() == 1 && calledFileNames == null) { + Util.log(IStatus.WARNING, "Invalid Class-Path header in manifest of jar file: " + jarPath.toOSString()); //$NON-NLS-1$ + return; + } else if (analyzer.getClasspathSectionsCount() > 1) { + Util.log(IStatus.WARNING, "Multiple Class-Path headers in manifest of jar file: " + jarPath.toOSString()); //$NON-NLS-1$ + return; + } + if (calledFileNames != null) { + Iterator calledFilesIterator = calledFileNames.iterator(); + IPath directoryPath = jarPath.removeLastSegments(1); + while (calledFilesIterator.hasNext()) { + String calledFileName = (String) calledFilesIterator.next(); + if (!directoryPath.isValidPath(calledFileName)) { + Util.log(IStatus.WARNING, "Invalid Class-Path entry " + calledFileName + " in manifest of jar file: " + jarPath.toOSString()); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + IPath calledJar = directoryPath.append(new Path(calledFileName)); + resolvedChainedLibraries(calledJar, visited, result); + result.add(calledJar); + } + } + } + } + } catch (CoreException e) { + // not a zip file + } catch (IOException e) { + Util.log(e, "Could not read Class-Path header in manifest of jar file: " + jarPath.toOSString()); //$NON-NLS-1$ + } finally { + manager.closeZipFile(zip); + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // best effort + } + } + } + } + } + + /* * Resolves the ".." in the given path. Returns the given path if it contains no ".." segment. */ public static IPath resolveDotDot(IPath path) { @@ -1163,7 +1242,11 @@ */ public String toString() { StringBuffer buffer = new StringBuffer(); - buffer.append(String.valueOf(getPath())); + Object target = JavaModel.getTarget(getPath(), true); + if (target instanceof File) + buffer.append(getPath().toOSString()); + else + buffer.append(String.valueOf(getPath())); buffer.append('['); switch (getEntryKind()) { case IClasspathEntry.CPE_LIBRARY : @@ -1278,6 +1361,34 @@ this.extraAttributes); } + /* + * Read the Class-Path clause of the manifest of the jar pointed by this entry, and return + * the corresponding library entries. + */ + public ClasspathEntry[] resolvedChainedLibraries() { + IPath[] paths = resolvedChainedLibraries(getPath()); + int length = paths.length; + if (length == 0) + return NO_ENTRIES; + ClasspathEntry[] result = new ClasspathEntry[length]; + for (int i = 0; i < length; i++) { + result[i] = new ClasspathEntry( + getContentKind(), + getEntryKind(), + paths[i], + this.inclusionPatterns, + this.exclusionPatterns, + getSourceAttachmentPath(), + getSourceAttachmentRootPath(), + getOutputLocation(), + this.isExported, + getAccessRules(), + this.combineAccessRules, + this.extraAttributes); + } + return result; + } + /** * Answers an ID which is used to distinguish entries during package * fragment root computations @@ -1614,7 +1725,7 @@ * * @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 checkSourceAttachment a flag to determine if source attachment 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 */ @@ -1625,8 +1736,7 @@ // Build some common strings for status message String projectName = project.getElementName(); - boolean pathStartsWithProject = projectName.equals(path.segment(0)); - String entryPathMsg = pathStartsWithProject ? path.removeFirstSegments(1).makeRelative().toString() : path.toString(); + String entryPathMsg = projectName.equals(path.segment(0)) ? path.removeFirstSegments(1).makeRelative().toString() : path.toString(); switch(entry.getEntryKind()){ @@ -1719,56 +1829,19 @@ // library entry check case IClasspathEntry.CPE_LIBRARY : path = ClasspathEntry.resolveDotDot(path); - if (path.isAbsolute() && !path.isEmpty()) { - IPath sourceAttachment = entry.getSourceAttachmentPath(); - Object target = JavaModel.getTarget(path, true); - if (target != null && !JavaCore.IGNORE.equals(project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true))) { - 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 (checkSourceAttachment - && sourceAttachment != null - && !sourceAttachment.isEmpty() - && JavaModel.getTarget(sourceAttachment, true) == null){ - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toString(), projectName})); - } - break; - case IResource.FOLDER : // internal binary folder - if (checkSourceAttachment - && sourceAttachment != null - && !sourceAttachment.isEmpty() - && JavaModel.getTarget(sourceAttachment, true) == null){ - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toString(), projectName})); - } - } - } else if (target instanceof File){ - File file = JavaModel.getFile(target); - if (file == null) { - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalExternalFolder, new String[] {path.toOSString(), projectName})); - } else if (checkSourceAttachment - && sourceAttachment != null - && !sourceAttachment.isEmpty() - && JavaModel.getTarget(sourceAttachment, true) == null){ - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toOSString(), projectName})); - } - } else { - boolean isExternal = path.getDevice() != null || !workspaceRoot.getProject(path.segment(0)).exists(); - if (isExternal) { - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String[] {path.toOSString(), projectName})); - } else { - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String[] {entryPathMsg, projectName})); - } - } - } else { - return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryPath, new String[] {entryPathMsg, projectName})); - } + + // resolve Class-Path: in manifest + IPath[] chainedJars = ClasspathEntry.resolvedChainedLibraries(path); + for (int i = 0, length = chainedJars.length; i < length; i++) { + IPath chainedJar = chainedJars[i]; + IJavaModelStatus status = validateLibraryEntry(chainedJar, project, null/*don't check source attachment*/, null/*force computing of entryPathMsg*/); + if (!status.isOK()) + return status; + } + + IJavaModelStatus status = validateLibraryEntry(path, project, checkSourceAttachment ? entry.getSourceAttachmentPath() : null, entryPathMsg); + if (!status.isOK()) + return status; break; // project entry check @@ -1834,4 +1907,58 @@ return JavaModelStatus.VERIFIED_OK; } + + private static IJavaModelStatus validateLibraryEntry(IPath path, IJavaProject project, IPath sourceAttachment, String entryPathMsg) { + if (path.isAbsolute() && !path.isEmpty()) { + Object target = JavaModel.getTarget(path, true); + if (target != null && !JavaCore.IGNORE.equals(project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true))) { + 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 (sourceAttachment != null + && !sourceAttachment.isEmpty() + && JavaModel.getTarget(sourceAttachment, true) == null){ + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toString(), project.getElementName()})); + } + break; + case IResource.FOLDER : // internal binary folder + if (sourceAttachment != null + && !sourceAttachment.isEmpty() + && JavaModel.getTarget(sourceAttachment, true) == null){ + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toString(), project.getElementName()})); + } + } + } else if (target instanceof File){ + File file = JavaModel.getFile(target); + if (file == null) { + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalExternalFolder, new String[] {path.toOSString(), project.getElementName()})); + } else if (sourceAttachment != null + && !sourceAttachment.isEmpty() + && JavaModel.getTarget(sourceAttachment, true) == null){ + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toOSString(), project.getElementName()})); + } + } else { + boolean isExternal = path.getDevice() != null || !ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0)).exists(); + if (isExternal) { + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String[] {path.toOSString(), project.getElementName()})); + } else { + if (entryPathMsg == null) + entryPathMsg = project.getElementName().equals(path.segment(0)) ? path.removeFirstSegments(1).makeRelative().toString() : path.toString(); + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String[] {entryPathMsg, project.getElementName()})); + } + } + } else { + if (entryPathMsg == null) + entryPathMsg = project.getElementName().equals(path.segment(0)) ? path.removeFirstSegments(1).makeRelative().toString() : path.toString(); + return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryPath, new String[] {entryPathMsg, project.getElementName()})); + } + return JavaModelStatus.VERIFIED_OK; + } } Index: model/org/eclipse/jdt/internal/core/JavaModelManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java,v retrieving revision 1.413 diff -u -r1.413 JavaModelManager.java --- model/org/eclipse/jdt/internal/core/JavaModelManager.java 26 Sep 2008 10:20:18 -0000 1.413 +++ model/org/eclipse/jdt/internal/core/JavaModelManager.java 7 Oct 2008 13:28:55 -0000 @@ -2178,7 +2178,7 @@ return containerIDs; } - public IClasspathEntry getResolvedClasspathEntry(IClasspathEntry entry, boolean usePreviousSession) { + public IClasspathEntry resolveVariableEntry(IClasspathEntry entry, boolean usePreviousSession) { if (entry.getEntryKind() != IClasspathEntry.CPE_VARIABLE) return entry; Index: model/org/eclipse/jdt/internal/core/DeltaProcessor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java,v retrieving revision 1.329 diff -u -r1.329 DeltaProcessor.java --- model/org/eclipse/jdt/internal/core/DeltaProcessor.java 9 Sep 2008 14:53:17 -0000 1.329 +++ model/org/eclipse/jdt/internal/core/DeltaProcessor.java 7 Oct 2008 13:28:54 -0000 @@ -313,7 +313,7 @@ * In all cases, add the project's dependents to the list of projects to update * so that the classpath related markers can be updated. */ - private void checkProjectsBeingAddedOrRemoved(IResourceDelta delta) { + private void checkProjectsAndClasspathChanges(IResourceDelta delta) { IResource resource = delta.getResource(); IResourceDelta[] children = null; @@ -438,12 +438,19 @@ } break; + case IResource.FOLDER: + if (delta.getKind() == IResourceDelta.CHANGED) { // look for .jar file change to update classpath + children = delta.getAffectedChildren(); + } + break; case IResource.FILE : IFile file = (IFile) resource; - /* classpath file change */ + int kind = delta.getKind(); + RootInfo rootInfo; if (file.getName().equals(JavaProject.CLASSPATH_FILENAME)) { + /* classpath file change */ this.manager.forceBatchInitializations(false/*not initAfterLoad*/); - switch (delta.getKind()) { + switch (kind) { case IResourceDelta.CHANGED : int flags = delta.getFlags(); if ((flags & IResourceDelta.CONTENT) == 0 // only consider content change @@ -463,13 +470,17 @@ break; } this.state.rootsAreStale = true; + } else if ((rootInfo = rootInfo(file.getFullPath(), kind)) != null && rootInfo.entryKind == IClasspathEntry.CPE_LIBRARY) { + javaProject = (JavaProject)JavaCore.create(file.getProject()); + javaProject.resetResolvedClasspath(); + this.state.rootsAreStale = true; } break; } if (children != null) { for (int i = 0; i < children.length; i++) { - checkProjectsBeingAddedOrRemoved(children[i]); + checkProjectsAndClasspathChanges(children[i]); } } } @@ -815,15 +826,15 @@ javaProject = (JavaProject) JavaCore.create(project); try { classpath = javaProject.getResolvedClasspath(); + for (int k = 0, cpLength = classpath.length; k < cpLength; k++){ + if (classpath[k].getEntryKind() == IClasspathEntry.CPE_LIBRARY){ + archivePathsToRefresh.add(classpath[k].getPath()); + } + } } catch (JavaModelException e2) { // project doesn't exist -> ignore continue; } - for (int k = 0, cpLength = classpath.length; k < cpLength; k++){ - if (classpath[k].getEntryKind() == IClasspathEntry.CPE_LIBRARY){ - archivePathsToRefresh.add(classpath[k].getPath()); - } - } } break; } @@ -852,7 +863,6 @@ } for (int j = 0; j < entries.length; j++){ if (entries[j].getEntryKind() == IClasspathEntry.CPE_LIBRARY) { - IPath entryPath = entries[j].getPath(); if (!archivePathsToRefresh.contains(entryPath)) continue; // not supposed to be refreshed @@ -919,6 +929,7 @@ System.out.println("- External JAR ADDED, affecting root: "+root.getElementName()); //$NON-NLS-1$ } elementAdded(root, null, null); + javaProject.resetResolvedClasspath(); // in case it contains a chained jar this.state.addClasspathValidation(javaProject); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=185733 hasDelta = true; } else if (status == EXTERNAL_JAR_CHANGED) { @@ -927,6 +938,7 @@ System.out.println("- External JAR CHANGED, affecting root: "+root.getElementName()); //$NON-NLS-1$ } contentChanged(root); + javaProject.resetResolvedClasspath(); // in case it contains a chained jar hasDelta = true; } else if (status == EXTERNAL_JAR_REMOVED) { PackageFragmentRoot root = (PackageFragmentRoot) javaProject.getPackageFragmentRoot(entryPath.toString()); @@ -934,6 +946,7 @@ System.out.println("- External JAR REMOVED, affecting root: "+root.getElementName()); //$NON-NLS-1$ } elementRemoved(root, null, null); + javaProject.resetResolvedClasspath(); // in case it contains a chained jar this.state.addClasspathValidation(javaProject); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=185733 hasDelta = true; } @@ -1894,7 +1907,12 @@ try { try { stopDeltas(); - checkProjectsBeingAddedOrRemoved(delta); + checkProjectsAndClasspathChanges(delta); + + // generate external archive change deltas + if (elementsToRefresh != null) { + createExternalArchiveDelta(elementsToRefresh, null); + } // generate classpath change deltas if (this.classpathChanges.size() > 0) { @@ -1926,13 +1944,7 @@ } // add late coming elements to refresh (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=212769 ) - if (elementsToRefresh == null) { - elementsToRefresh = this.state.removeExternalElementsToRefresh(); - } else { - HashSet newElementsToRefresh = this.state.removeExternalElementsToRefresh(); - if (newElementsToRefresh != null) - elementsToRefresh.addAll(newElementsToRefresh); - } + elementsToRefresh = this.state.removeExternalElementsToRefresh(); // generate external archive change deltas if (elementsToRefresh != null) { Index: model/org/eclipse/jdt/core/JavaCore.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java,v retrieving revision 1.627 diff -u -r1.627 JavaCore.java --- model/org/eclipse/jdt/core/JavaCore.java 3 Oct 2008 08:39:29 -0000 1.627 +++ model/org/eclipse/jdt/core/JavaCore.java 7 Oct 2008 13:28:53 -0000 @@ -3234,7 +3234,7 @@ * if the given variable entry could not be resolved to a valid classpath entry */ public static IClasspathEntry getResolvedClasspathEntry(IClasspathEntry entry) { - return JavaModelManager.getJavaModelManager().getResolvedClasspathEntry(entry, false/*don't use previous session value*/); + return JavaModelManager.getJavaModelManager().resolveVariableEntry(entry, false/*don't use previous session value*/); } @@ -3929,6 +3929,10 @@ * accessible files patterns of the projects, and they will concatenate the non accessible files patterns of this entry * with the non accessible files patterns of the project. *

+ *

+ * Since 3.5, if the libray is a ZIP archive, the "Class-Path" clause (if any) in the "META-INF/MANIFEST.MF" is read + * and referenced ZIP archives are added to the {@link IJavaProject#getResolvedClasspath(boolean) resolved classpath}. + *

* * @param path the path to the library * @param sourceAttachmentPath the absolute path of the corresponding source archive or folder, Index: compiler/org/eclipse/jdt/internal/compiler/util/ManifestAnalyzer.java =================================================================== RCS file: compiler/org/eclipse/jdt/internal/compiler/util/ManifestAnalyzer.java diff -N compiler/org/eclipse/jdt/internal/compiler/util/ManifestAnalyzer.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compiler/org/eclipse/jdt/internal/compiler/util/ManifestAnalyzer.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.util; + +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +public class ManifestAnalyzer { + private static final int + START = 0, + IN_CLASSPATH_HEADER = 1, // multistate + PAST_CLASSPATH_HEADER = 2, + SKIPPING_WHITESPACE = 3, + READING_JAR = 4, + CONTINUING = 5, + SKIP_LINE = 6; + private static final char[] CLASSPATH_HEADER_TOKEN = + "Class-Path:".toCharArray(); //$NON-NLS-1$ + private int ClasspathSectionsCount; + private ArrayList calledFilesNames; + public boolean analyzeManifestContents(Reader reader) throws IOException { + int state = START, substate = 0; + StringBuffer currentJarToken = new StringBuffer(); + int currentChar; + this.ClasspathSectionsCount = 0; + this.calledFilesNames = null; + for (;;) { + currentChar = reader.read(); + switch (state) { + case START: + if (currentChar == -1) { + return true; + } else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) { + state = IN_CLASSPATH_HEADER; + substate = 1; + } else { + state = SKIP_LINE; + } + break; + case IN_CLASSPATH_HEADER: + if (currentChar == -1) { + return true; + } else if (currentChar == '\n') { + state = START; + } else if (currentChar != CLASSPATH_HEADER_TOKEN[substate++]) { + state = SKIP_LINE; + } else if (substate == CLASSPATH_HEADER_TOKEN.length) { + state = PAST_CLASSPATH_HEADER; + } + break; + case PAST_CLASSPATH_HEADER: + if (currentChar == ' ') { + state = SKIPPING_WHITESPACE; + this.ClasspathSectionsCount++; + } else { + return false; + } + break; + case SKIPPING_WHITESPACE: + if (currentChar == -1) { + return true; + } else if (currentChar == '\n') { + state = CONTINUING; + } else if (currentChar != ' ') { + currentJarToken.append((char) currentChar); + state = READING_JAR; + } + break; + case CONTINUING: + if (currentChar == -1) { + return true; + } else if (currentChar == '\n') { + state = START; + } else if (currentChar == ' ') { + state = SKIPPING_WHITESPACE; + } else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) { + state = IN_CLASSPATH_HEADER; + substate = 1; + } else if (this.calledFilesNames == null) { + return false; + } else { + state = SKIP_LINE; + } + break; + case SKIP_LINE: + if (currentChar == -1) { + return true; + } else if (currentChar == '\n') { + state = START; + } + break; + case READING_JAR: + if (currentChar == -1) { + return false; + } else if (currentChar == '\n' || currentChar == '\r') { + // appends token below + state = CONTINUING; + } else if (currentChar == ' ') { + // appends token below + state = SKIPPING_WHITESPACE; + } else { + currentJarToken.append((char) currentChar); + break; + } + if (this.calledFilesNames == null) { + this.calledFilesNames = new ArrayList(); + } + this.calledFilesNames.add(currentJarToken.toString()); + currentJarToken.setLength(0); + break; + } + } + } + public int getClasspathSectionsCount() { + return this.ClasspathSectionsCount; + } + public List getCalledFileNames() { + return this.calledFilesNames; + } +} \ No newline at end of file#P org.eclipse.jdt.core.tests.model Index: src/org/eclipse/jdt/core/tests/model/ReconcilerTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests.java,v retrieving revision 1.141 diff -u -r1.141 ReconcilerTests.java --- src/org/eclipse/jdt/core/tests/model/ReconcilerTests.java 26 Sep 2008 10:20:16 -0000 1.141 +++ src/org/eclipse/jdt/core/tests/model/ReconcilerTests.java 7 Oct 2008 13:28:59 -0000 @@ -309,10 +309,11 @@ "public class X {\n" + "}", }, + null/*no non-Java resources*/, new String[] { "**/*" }, - null, + null, "1.4" ); createJavaProject("P2", new String[] {"src"}, new String[] {"JCL_LIB"}, new String[] {"/P1"}, "bin"); Index: src/org/eclipse/jdt/core/tests/model/ClasspathTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java,v retrieving revision 1.187 diff -u -r1.187 ClasspathTests.java --- src/org/eclipse/jdt/core/tests/model/ClasspathTests.java 26 Sep 2008 09:01:25 -0000 1.187 +++ src/org/eclipse/jdt/core/tests/model/ClasspathTests.java 7 Oct 2008 13:28:58 -0000 @@ -2970,6 +2970,375 @@ } } /* + * Ensures that the extra libraries in the Class-Path: clause of a jar are taken into account. + */ +public void testExtraLibraries01() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createFile("/P/lib2.jar", ""); + assertClasspathEquals( + p.getResolvedClasspath(true), + "/P[CPE_SOURCE][K_SOURCE][isExported:false]\n" + + ""+ getExternalJCLPathString() + "[CPE_LIBRARY][K_BINARY][isExported:false]\n" + + "/P/lib2.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib1.jar[CPE_LIBRARY][K_BINARY][isExported:true]" + ); + } finally { + deleteProject("P"); + } +} +/* + * Ensures that the extra libraries in the Class-Path: clause of a jar are taken into account. + */ +public void testExtraLibraries02() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar lib3.jar\n", + }, + JavaCore.VERSION_1_4); + createFile("/P/lib2.jar", ""); + createFile("/P/lib3.jar", ""); + assertClasspathEquals( + p.getResolvedClasspath(true), + "/P[CPE_SOURCE][K_SOURCE][isExported:false]\n" + + ""+ getExternalJCLPathString() + "[CPE_LIBRARY][K_BINARY][isExported:false]\n" + + "/P/lib2.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib3.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib1.jar[CPE_LIBRARY][K_BINARY][isExported:true]" + ); + } finally { + deleteProject("P"); + } +} +/* + * Ensures that the extra libraries in the Class-Path: clause of a jar are taken into account. + */ +public void testExtraLibraries03() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + createFile("/P/lib3.jar", ""); + createLibrary(p, "lib2.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib3.jar\n", + }, + JavaCore.VERSION_1_4); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + assertClasspathEquals( + p.getResolvedClasspath(true), + "/P[CPE_SOURCE][K_SOURCE][isExported:false]\n" + + ""+ getExternalJCLPathString() + "[CPE_LIBRARY][K_BINARY][isExported:false]\n" + + "/P/lib3.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib2.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib1.jar[CPE_LIBRARY][K_BINARY][isExported:true]" + ); + } finally { + deleteProject("P"); + } +} +/* + * Ensures that the extra libraries in the Class-Path: clause of a jar are taken into account. + */ +public void testExtraLibraries04() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createLibrary(p, "lib2.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib3.jar\n", + }, + JavaCore.VERSION_1_4); + createFile("/P/lib3.jar", ""); + assertClasspathEquals( + p.getResolvedClasspath(true), + "/P[CPE_SOURCE][K_SOURCE][isExported:false]\n" + + ""+ getExternalJCLPathString() + "[CPE_LIBRARY][K_BINARY][isExported:false]\n" + + "/P/lib3.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib2.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + "/P/lib1.jar[CPE_LIBRARY][K_BINARY][isExported:true]" + ); + } finally { + deleteProject("P"); + } +} +/* + * Ensures that no markers are created for correct extra libraries in the Class-Path: clause of a jar + */ +public void testExtraLibraries05() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createFile("/P/lib2.jar", ""); + assertMarkers( + "Unexpected markers", + "", + p); + } finally { + deleteProject("P"); + } +} +/* + * Ensures that a marker is created for incorrect extra libraries in the Class-Path: clause of a jar + */ +public void testExtraLibraries06() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + assertMarkers( + "Unexpected markers", + "Project \'P\' is missing required library: \'lib2.jar\'", + p); + } finally { + deleteProject("P"); + } +} +/* + * Ensures that the extra libraries in the Class-Path: clause of an external jar are taken into account. + */ +public void testExtraLibraries07() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addExternalLibrary(p, getExternalResourcePath("lib1.jar"), new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createExternalFile("lib2.jar", ""); + assertClasspathEquals( + p.getResolvedClasspath(true), + "/P[CPE_SOURCE][K_SOURCE][isExported:false]\n" + + ""+ getExternalJCLPathString() + "[CPE_LIBRARY][K_BINARY][isExported:false]\n" + + ""+ getExternalPath() + "lib2.jar[CPE_LIBRARY][K_BINARY][isExported:true]\n" + + ""+ getExternalPath() + "lib1.jar[CPE_LIBRARY][K_BINARY][isExported:true]" + ); + } finally { + deleteProject("P"); + deleteExternalResource("lib1.jar"); + deleteExternalResource("lib2.jar"); + } +} +/* + * Ensures that no markers are created for correct extra libraries in the Class-Path: clause of an external jar + */ +public void testExtraLibraries08() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addExternalLibrary(p, getExternalResourcePath("lib1.jar"), new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createExternalFile("lib2.jar", ""); + refreshExternalArchives(p); + assertMarkers( + "Unexpected markers", + "", + p); + } finally { + deleteProject("P"); + deleteExternalResource("lib1.jar"); + deleteExternalResource("lib2.jar"); + } +} +/* + * Ensures that a marker is created for incorrect extra libraries in the Class-Path: clause of an external jar + */ +public void testExtraLibraries09() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addExternalLibrary(p, getExternalResourcePath("lib1.jar"), new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + assertMarkers( + "Unexpected markers", + "Project \'P\' is missing required library: \'"+ getExternalPath() + "lib2.jar\'", + p); + } finally { + deleteProject("P"); + deleteExternalResource("lib1.jar"); + deleteExternalResource("lib2.jar"); + } +} +/* + * Ensures that a marker is created for incorrect extra libraries in the Class-Path: clause of an external jar + */ +public void testExtraLibraries10() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addExternalLibrary(p, getExternalResourcePath("lib1.jar"), new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createExternalFile("lib2.jar", ""); + refreshExternalArchives(p); + waitForAutoBuild(); // wait until classpath is validated -> no markers + + deleteExternalResource("lib2.jar"); + refreshExternalArchives(p); + assertMarkers( + "Unexpected markers", + "Project \'P\' is missing required library: \'"+ getExternalPath() + "lib2.jar\'", + p); + } finally { + deleteProject("P"); + deleteExternalResource("lib1.jar"); + deleteExternalResource("lib2.jar"); + } +} +/* + * Ensures that the correct delta is reported when editing the Class-Path: clause of a jar + */ +public void testExtraLibraries11() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createFile("/P/lib2.jar", ""); + startDeltas(); + createLibrary(p, "lib1.jar", null, new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib3.jar\n", + }, + JavaCore.VERSION_1_4); + assertDeltas( + "Unexpected delta", + "P[*]: {CHILDREN | RESOLVED CLASSPATH CHANGED}\n" + + " lib1.jar[*]: {CONTENT | ARCHIVE CONTENT CHANGED}\n" + + " lib2.jar[*]: {REMOVED FROM CLASSPATH}" + ); + } finally { + stopDeltas(); + deleteProject("P"); + } +} +/* + * Ensures that the correct delta is reported when editing the Class-Path: clause of an external jar + */ +public void testExtraLibraries12() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addExternalLibrary(p, getExternalResourcePath("lib1.jar"), new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createExternalFile("lib2.jar", ""); + refreshExternalArchives(p); + + startDeltas(); + org.eclipse.jdt.core.tests.util.Util.createJar(new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib3.jar\n", + }, + getExternalResourcePath("lib1.jar"), + JavaCore.VERSION_1_4); + refreshExternalArchives(p); + assertDeltas( + "Unexpected delta", + "P[*]: {CHILDREN | RESOLVED CLASSPATH CHANGED}\n" + + " "+ getExternalPath() + "lib1.jar[*]: {CONTENT | ARCHIVE CONTENT CHANGED}\n" + + " "+ getExternalPath() + "lib2.jar[*]: {REMOVED FROM CLASSPATH}" + ); + } finally { + stopDeltas(); + deleteProject("P"); + deleteExternalResource("lib1.jar"); + deleteExternalResource("lib2.jar"); + } +} +/* + * Ensures that the correct delta is reported when removing an external jar with a Class-Path: clause + */ +public void testExtraLibraries13() throws Exception { + try { + IJavaProject p = createJavaProject("P"); + addExternalLibrary(p, getExternalResourcePath("lib1.jar"), new String[0], + new String[] { + "META-INF/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib2.jar\n", + }, + JavaCore.VERSION_1_4); + createExternalFile("lib2.jar", ""); + refreshExternalArchives(p); + + startDeltas(); + deleteExternalResource("lib1.jar"); + refreshExternalArchives(p); + assertDeltas( + "Unexpected delta", + "P[*]: {CHILDREN | RESOLVED CLASSPATH CHANGED}\n" + + " "+ getExternalPath() + "lib1.jar[-]: {}\n" + + " "+ getExternalPath() + "lib2.jar[*]: {REMOVED FROM CLASSPATH}" + ); + } finally { + stopDeltas(); + deleteProject("P"); + deleteExternalResource("lib1.jar"); + deleteExternalResource("lib2.jar"); + } +} +/* * Ensures that a marker is removed if adding an internal jar that is on the classpath in another project * (regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=213723 ) */ Index: src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java,v retrieving revision 1.222 diff -u -r1.222 AbstractJavaModelTests.java --- src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java 26 Sep 2008 09:01:25 -0000 1.222 +++ src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java 7 Oct 2008 13:28:57 -0000 @@ -391,31 +391,45 @@ ); } + protected void addExternalLibrary(IJavaProject javaProject, String jarPath, String[] pathAndContents, String[] nonJavaResources, String compliance) throws Exception { + org.eclipse.jdt.core.tests.util.Util.createJar(pathAndContents, nonJavaResources, jarPath, compliance); + addLibraryEntry(javaProject, new Path(jarPath), true/*exported*/); + } protected void addLibrary(String jarName, String sourceZipName, String[] pathAndContents, String compliance) throws CoreException, IOException { - addLibrary(this.currentProject, jarName, sourceZipName, pathAndContents, null, null, compliance); + addLibrary(this.currentProject, jarName, sourceZipName, pathAndContents, null/*no non-Java resources*/, null, null, compliance); } protected void addLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents, String compliance) throws CoreException, IOException { - addLibrary(javaProject, jarName, sourceZipName, pathAndContents, null, null, compliance); + addLibrary(javaProject, jarName, sourceZipName, pathAndContents, null/*no non-Java resources*/, null, null, compliance); } - protected void addLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents, String[] librariesInclusionPatterns, String[] librariesExclusionPatterns, String compliance) throws CoreException, IOException { - IProject project = javaProject.getProject(); - String projectLocation = project.getLocation().toOSString(); - String jarPath = projectLocation + File.separator + jarName; - String sourceZipPath = projectLocation + File.separator + sourceZipName; - org.eclipse.jdt.core.tests.util.Util.createJar(pathAndContents, jarPath, compliance); - org.eclipse.jdt.core.tests.util.Util.createSourceZip(pathAndContents, sourceZipPath); - project.refreshLocal(IResource.DEPTH_INFINITE, null); + protected void addLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents, String[] nonJavaResources, String compliance) throws CoreException, IOException { + addLibrary(javaProject, jarName, sourceZipName, pathAndContents, nonJavaResources, null, null, compliance); + } + protected void addLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents, String[] nonJavaResources, String[] librariesInclusionPatterns, String[] librariesExclusionPatterns, String compliance) throws CoreException, IOException { + IProject project = createLibrary(javaProject, jarName, sourceZipName, pathAndContents, nonJavaResources, compliance); String projectPath = '/' + project.getName() + '/'; addLibraryEntry( javaProject, new Path(projectPath + jarName), - new Path(projectPath + sourceZipName), + sourceZipName == null ? null : new Path(projectPath + sourceZipName), null, toIPathArray(librariesInclusionPatterns), toIPathArray(librariesExclusionPatterns), true ); } + + protected IProject createLibrary(IJavaProject javaProject, String jarName, String sourceZipName, String[] pathAndContents, String[] nonJavaResources, String compliance) throws IOException, CoreException { + IProject project = javaProject.getProject(); + String projectLocation = project.getLocation().toOSString(); + String jarPath = projectLocation + File.separator + jarName; + org.eclipse.jdt.core.tests.util.Util.createJar(pathAndContents, nonJavaResources, jarPath, compliance); + if (pathAndContents != null && pathAndContents.length != 0) { + String sourceZipPath = projectLocation + File.separator + sourceZipName; + org.eclipse.jdt.core.tests.util.Util.createSourceZip(pathAndContents, sourceZipPath); + } + project.refreshLocal(IResource.DEPTH_INFINITE, null); + return project; + } protected void addLibraryEntry(String path, boolean exported) throws JavaModelException { addLibraryEntry(this.currentProject, new Path(path), null, null, null, null, exported); } @@ -878,7 +892,7 @@ actual = buffer.toString(); } if (!actual.equals(expected)) { - System.out.print(org.eclipse.jdt.core.tests.util.Util.displayString(actual, 2)); + System.out.print(displayString(actual, 2)); } assertEquals(expected, actual); }