### Eclipse Workspace Patch 1.0
#P org.eclipse.jdt.core
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.414
diff -u -r1.414 JavaProject.java
--- model/org/eclipse/jdt/internal/core/JavaProject.java 22 Sep 2008 11:32:43 -0000 1.414
+++ model/org/eclipse/jdt/internal/core/JavaProject.java 25 Sep 2008 14:15:29 -0000
@@ -267,24 +267,14 @@
if (externalPath == null)
return null;
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonicalizing " + externalPath.toString());
-// }
-
if (IS_CASE_SENSITIVE) {
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonical path is original path (file system is case sensitive)");
-// }
return externalPath;
}
-
+
// if not external path, return original path
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (workspace == null) return externalPath; // protection during shutdown (30487)
if (workspace.getRoot().findMember(externalPath) != null) {
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonical path is original path (member of workspace)");
-// }
return externalPath;
}
@@ -294,9 +284,6 @@
new Path(new File(externalPath.toOSString()).getCanonicalPath());
} catch (IOException e) {
// default to original path
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonical path is original path (IOException)");
-// }
return externalPath;
}
@@ -304,9 +291,6 @@
int canonicalLength = canonicalPath.segmentCount();
if (canonicalLength == 0) {
// the java.io.File canonicalization failed
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonical path is original path (canonical path is empty)");
-// }
return externalPath;
} else if (externalPath.isAbsolute()) {
result = canonicalPath;
@@ -317,9 +301,6 @@
if (canonicalLength >= externalLength) {
result = canonicalPath.removeFirstSegments(canonicalLength - externalLength);
} else {
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonical path is original path (canonical path is " + canonicalPath.toString() + ")");
-// }
return externalPath;
}
}
@@ -332,9 +313,6 @@
if (externalPath.hasTrailingSeparator()) {
result = result.addTrailingSeparator();
}
-// if (JavaModelManager.VERBOSE) {
-// System.out.println("JAVA MODEL - Canonical path is " + result.toString());
-// }
return result;
}
@@ -2569,6 +2547,10 @@
}
// 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) {
+ cEntry = cEntry.resolvedDotDot();
+ }
if (result.rawReverseMap.get(resolvedPath = cEntry.getPath()) == null) {
result.rawReverseMap.put(resolvedPath , rawEntry);
result.rootPathToResolvedEntries.put(resolvedPath, cEntry);
@@ -2580,6 +2562,9 @@
}
break;
+ case IClasspathEntry.CPE_LIBRARY:
+ rawEntry = ((ClasspathEntry) rawEntry).resolvedDotDot();
+ // $FALL-THROUGH$ use the default code below
default :
if (result.rawReverseMap.get(resolvedPath = rawEntry.getPath()) == null) {
result.rawReverseMap.put(resolvedPath , rawEntry);
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.103
diff -u -r1.103 ClasspathEntry.java
--- model/org/eclipse/jdt/internal/core/ClasspathEntry.java 1 Sep 2008 08:53:56 -0000 1.103
+++ model/org/eclipse/jdt/internal/core/ClasspathEntry.java 25 Sep 2008 14:15:28 -0000
@@ -196,6 +196,8 @@
* A constant indicating an output location.
*/
public static final int K_OUTPUT = 10;
+
+ public static final String DOT_DOT = ".."; //$NON-NLS-1$
/**
* The export flag
@@ -625,7 +627,9 @@
IPath path = new Path(pathAttr);
int kind = kindFromString(kindAttr);
if (kind != IClasspathEntry.CPE_VARIABLE && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
- path = projectPath.append(path);
+ if (!(path.segmentCount() > 0 && path.segment(0).equals(ClasspathEntry.DOT_DOT))) {
+ path = projectPath.append(path);
+ }
}
// source attachment info (optional)
IPath sourceAttachmentPath =
@@ -705,18 +709,18 @@
case IClasspathEntry.CPE_PROJECT :
entry = new ClasspathEntry(
- IPackageFragmentRoot.K_SOURCE,
- IClasspathEntry.CPE_PROJECT,
- path,
- ClasspathEntry.INCLUDE_ALL, // inclusion patterns
- ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
- null, // source attachment
- null, // source attachment root
- null, // specific output folder
- isExported,
- accessRules,
- combineAccessRestrictions,
- extraAttributes);
+ IPackageFragmentRoot.K_SOURCE,
+ IClasspathEntry.CPE_PROJECT,
+ path,
+ ClasspathEntry.INCLUDE_ALL, // inclusion patterns
+ ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
+ null, // source attachment
+ null, // source attachment root
+ null, // specific output folder
+ isExported,
+ accessRules,
+ combineAccessRestrictions,
+ extraAttributes);
break;
case IClasspathEntry.CPE_LIBRARY :
entry = JavaCore.newLibraryEntry(
@@ -731,7 +735,12 @@
// 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
- entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes);
+ entry = JavaCore.newSourceEntry(
+ path,
+ inclusionPatterns,
+ exclusionPatterns,
+ outputLocation,
+ extraAttributes);
} else {
if (path.segmentCount() == 1) {
// another project
@@ -743,41 +752,46 @@
isExported);
} else {
// an invalid source folder
- entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes);
+ entry = JavaCore.newSourceEntry(
+ path,
+ inclusionPatterns,
+ exclusionPatterns,
+ outputLocation,
+ extraAttributes);
}
}
break;
case IClasspathEntry.CPE_VARIABLE :
entry = JavaCore.newVariableEntry(
- path,
- sourceAttachmentPath,
- sourceAttachmentRootPath,
- accessRules,
- extraAttributes,
- isExported);
+ path,
+ sourceAttachmentPath,
+ sourceAttachmentRootPath,
+ accessRules,
+ extraAttributes,
+ isExported);
break;
case IClasspathEntry.CPE_CONTAINER :
entry = JavaCore.newContainerEntry(
- path,
- accessRules,
- extraAttributes,
- isExported);
+ path,
+ accessRules,
+ extraAttributes,
+ isExported);
break;
case ClasspathEntry.K_OUTPUT :
if (!path.isAbsolute()) return null;
entry = new ClasspathEntry(
- ClasspathEntry.K_OUTPUT,
- IClasspathEntry.CPE_LIBRARY,
- path,
- INCLUDE_ALL,
- EXCLUDE_NONE,
- null, // source attachment
- null, // source attachment root
- null, // custom output location
- false,
- null, // no access rules
- false, // no accessible files to combine
- NO_EXTRA_ATTRIBUTES);
+ ClasspathEntry.K_OUTPUT,
+ IClasspathEntry.CPE_LIBRARY,
+ path,
+ INCLUDE_ALL,
+ EXCLUDE_NONE,
+ null, // source attachment
+ null, // source attachment root
+ null, // custom output location
+ false,
+ null, // no access rules
+ false, // no accessible files to combine
+ NO_EXTRA_ATTRIBUTES);
break;
default :
throw new AssertionFailedException(Messages.bind(Messages.classpath_unknownKind, kindAttr));
@@ -792,6 +806,17 @@
return entry;
}
+
+ /*
+ * Returns whether the given path as a ".." segment
+ */
+ public static boolean hasDotDot(IPath path) {
+ for (int i = 0, length = path.segmentCount(); i < length; i++) {
+ if (DOT_DOT.equals(path.segment(i)))
+ return true;
+ }
+ return false;
+ }
public static NodeList getChildAttributes(String childName, NodeList children, boolean[] foundChildren) {
for (int i = 0, length = foundChildren.length; i < length; i++) {
@@ -822,6 +847,44 @@
}
}
+ /*
+ * Resolves the ".." in the given path. Returns the given path if it contains no ".." segment.
+ */
+ public static IPath resolveDotDot(IPath path) {
+ IPath newPath = null;
+ IWorkspaceRoot root = null;
+ IPath workspaceLocation = null;
+ for (int i = 0, length = path.segmentCount(); i < length; i++) {
+ String segment = path.segment(i);
+ if (DOT_DOT.equals(segment)) {
+ if (newPath == null) {
+ if (i == 0) {
+ workspaceLocation = (root = ResourcesPlugin.getWorkspace().getRoot()).getLocation();
+ newPath = workspaceLocation;
+ } else {
+ newPath = path.removeFirstSegments(i);
+ }
+ } else {
+ if (newPath.segmentCount() > 0) {
+ newPath = newPath.removeLastSegments(1);
+ } else {
+ workspaceLocation = (root = ResourcesPlugin.getWorkspace().getRoot()).getLocation();
+ newPath = workspaceLocation;
+ }
+ }
+ } else if (newPath != null) {
+ if (newPath.equals(workspaceLocation) && root.getProject(segment).isAccessible()) {
+ newPath = new Path(segment).makeAbsolute();
+ } else {
+ newPath = newPath.append(segment);
+ }
+ }
+ }
+ if (newPath == null)
+ return path;
+ return newPath;
+ }
+
/**
* Encode some patterns into XML parameter tag
*/
@@ -1195,7 +1258,26 @@
}
return buffer.toString();
}
-
+
+ public ClasspathEntry resolvedDotDot() {
+ IPath resolvedPath = resolveDotDot(this.path);
+ if (resolvedPath == this.path)
+ return this;
+ return new ClasspathEntry(
+ getContentKind(),
+ getEntryKind(),
+ resolvedPath,
+ this.inclusionPatterns,
+ this.exclusionPatterns,
+ getSourceAttachmentPath(),
+ getSourceAttachmentRootPath(),
+ getOutputLocation(),
+ this.isExported,
+ getAccessRules(),
+ this.combineAccessRules,
+ this.extraAttributes);
+ }
+
/**
* Answers an ID which is used to distinguish entries during package
* fragment root computations
@@ -1636,6 +1718,7 @@
// 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);
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.411
diff -u -r1.411 JavaModelManager.java
--- model/org/eclipse/jdt/internal/core/JavaModelManager.java 12 Sep 2008 15:42:59 -0000 1.411
+++ model/org/eclipse/jdt/internal/core/JavaModelManager.java 25 Sep 2008 14:15:29 -0000
@@ -2186,6 +2186,7 @@
IPath resolvedPath = getResolvedVariablePath(entry.getPath(), usePreviousSession);
if (resolvedPath == null)
return null;
+ resolvedPath = ClasspathEntry.resolveDotDot(resolvedPath);
Object target = JavaModel.getTarget(resolvedPath, false);
if (target == 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.625
diff -u -r1.625 JavaCore.java
--- model/org/eclipse/jdt/core/JavaCore.java 5 Sep 2008 15:41:12 -0000 1.625
+++ model/org/eclipse/jdt/core/JavaCore.java 25 Sep 2008 14:15:28 -0000
@@ -3825,7 +3825,7 @@
* {@link #newLibraryEntry(IPath, IPath, IPath, IAccessRule[], IClasspathAttribute[], boolean)
* newLibraryEntry(path, sourceAttachmentPath, sourceAttachmentRootPath, new IAccessRule[0], new IClasspathAttribute[0], false)}.
*
- * @param path the absolute path of the binary archive
+ * @param path the path to the library
* @param sourceAttachmentPath the absolute path of the corresponding source archive or folder,
* or null
if none. Note, since 3.0, an empty path is allowed to denote no source attachment.
* Since 3.4, this path can also denote a path external to the workspace.
@@ -3856,7 +3856,7 @@
* {@link #newLibraryEntry(IPath, IPath, IPath, IAccessRule[], IClasspathAttribute[], boolean)
* newLibraryEntry(path, sourceAttachmentPath, sourceAttachmentRootPath, new IAccessRule[0], new IClasspathAttribute[0], isExported)}.
*
- * @param path the absolute path of the binary archive
+ * @param path the path to the library
* @param sourceAttachmentPath the absolute path of the corresponding source archive or folder,
* or null
if none. Note, since 3.0, an empty path is allowed to denote no source attachment.
* and will be automatically converted to null
. Since 3.4, this path can also denote a path external
@@ -3893,12 +3893,14 @@
* to the workspace root), or externally to the workspace (absolute path in the file system).
* The target root folder can also be defined internally to the workspace (absolute path relative
* to the workspace root), or - since 3.4 - externally to the workspace (absolute path in the file system).
+ * Since 3.5, the path to the library can also be relative to the project using ".." as the first segment.
*
* e.g. Here are some examples of binary path usage
"c:\jdk1.2.2\jre\lib\rt.jar"
- reference to an external JAR on Windows "/Project/someLib.jar"
- reference to an internal JAR on Windows or Linux "/Project/classes/"
- reference to an internal binary folder on Windows or Linux "/home/usr/classes"
- reference to an external binary folder on Linux "../../lib/someLib.jar"
- reference to an external JAR that is a sibbling of the workspace on either platform"/some/lib.jar"
is ambiguous.
* It can be a path to an external JAR (its file system path being "/some/lib.jar"
)
@@ -3928,7 +3930,7 @@
* with the non accessible files patterns of the project.
*
*
- * @param path the absolute path of the binary archive
+ * @param path the path to the library
* @param sourceAttachmentPath the absolute path of the corresponding source archive or folder,
* or null
if none. Note, since 3.0, an empty path is allowed to denote no source attachment.
* and will be automatically converted to null
. Since 3.4, this path can also denote a path external
@@ -3951,7 +3953,8 @@
boolean isExported) {
if (path == null) throw new ClasspathEntry.AssertionFailedException("Library path cannot be null"); //$NON-NLS-1$
- if (!path.isAbsolute()) throw new ClasspathEntry.AssertionFailedException("Path for IClasspathEntry must be absolute: " + path); //$NON-NLS-1$
+ boolean hasDotDot = ClasspathEntry.hasDotDot(path);
+ if (!hasDotDot && !path.isAbsolute()) throw new ClasspathEntry.AssertionFailedException("Path for IClasspathEntry must be absolute: " + path); //$NON-NLS-1$
if (sourceAttachmentPath != null) {
if (sourceAttachmentPath.isEmpty()) {
sourceAttachmentPath = null; // treat empty path as none
@@ -3964,7 +3967,7 @@
return new ClasspathEntry(
IPackageFragmentRoot.K_BINARY,
IClasspathEntry.CPE_LIBRARY,
- JavaProject.canonicalizedPath(path),
+ hasDotDot ? path : JavaProject.canonicalizedPath(path),
ClasspathEntry.INCLUDE_ALL, // inclusion patterns
ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
sourceAttachmentPath,
@@ -4668,6 +4671,7 @@
/**
* Sets the value of the given classpath variable.
* The path must not be null.
+ * Since 3.5, the path to a library can also be relative to the project using ".." as the first segment.
* * This functionality cannot be used while the resource tree is locked. *
@@ -4695,6 +4699,7 @@ /** * Sets the values of all the given classpath variables at once. * Null paths can be used to request corresponding variable removal. + * Since 3.5, the path to a library can also be relative to the project using ".." as the first segment. *
* A combined Java element delta will be notified to describe the corresponding
* classpath changes resulting from the variables update. This operation is batched,
#P org.eclipse.jdt.core.tests.model
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.186
diff -u -r1.186 ClasspathTests.java
--- src/org/eclipse/jdt/core/tests/model/ClasspathTests.java 12 Sep 2008 15:42:57 -0000 1.186
+++ src/org/eclipse/jdt/core/tests/model/ClasspathTests.java 25 Sep 2008 14:15:32 -0000
@@ -2307,6 +2307,261 @@
}
}
+/*
+ * Ensures that one can point at an external library using a ".." entry in a cp container
+ */
+public void testDotDotContainerEntry1() throws Exception {
+ String externalJarPath = getWorkspaceRoot().getLocation().removeLastSegments(1).append("external.jar").toOSString();
+ try {
+ IJavaProject p = createJavaProject("P");
+ Util.writeToFile("", externalJarPath);
+ ContainerInitializer.setInitializer(new DefaultContainerInitializer(new String[] {"P", "../../external.jar"}));
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newContainerEntry(new Path("org.eclipse.jdt.core.tests.model.TEST_CONTAINER"))});
+ assertElementDescendants(
+ "Unexpected project content",
+ "P\n" +
+ " "+ getExternalPath() + "external.jar",
+ p
+ );
+ } finally {
+ deleteResource(new File(externalJarPath));
+ deleteProject("P");
+ ContainerInitializer.setInitializer(null);
+ }
+}
+
+/*
+ * Ensures that a marker is created if one can point at a non-existing external library using a ".." entry in a cp container
+ */
+public void testDotDotContainerEntry2() throws Exception {
+ try {
+ IJavaProject p = createJavaProject("P");
+ ContainerInitializer.setInitializer(new DefaultContainerInitializer(new String[] {"P", "../../nonExisting.jar"}));
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newContainerEntry(new Path("org.eclipse.jdt.core.tests.model.TEST_CONTAINER"))});
+ assertMarkers(
+ "Unexpected markers",
+ "Project \'P\' is missing required library: \'"+ getExternalPath() + "nonExisting.jar\'",
+ p);
+ } finally {
+ deleteProject("P");
+ ContainerInitializer.setInitializer(null);
+ }
+}
+
+/*
+ * Ensures that one can point at an external library using a ".." path
+ */
+public void testDotDotLibraryEntry1() throws Exception {
+ String externalJarPath = getWorkspaceRoot().getLocation().append("external.jar").toOSString();
+ try {
+ IJavaProject p = createJavaProject("P");
+ Util.writeToFile("", externalJarPath);
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newLibraryEntry(new Path("../external.jar"), null, null)});
+ assertElementDescendants(
+ "Unexpected project content",
+ "P\n" +
+ " "+ getWorkspacePath() + "external.jar",
+ p
+ );
+ } finally {
+ deleteResource(new File(externalJarPath));
+ deleteProject("P");
+ }
+}
+
+/*
+ * Ensures that one can point at an external library using a ".." path
+ */
+public void testDotDotLibraryEntry2() throws Exception {
+ String externalJarPath = getWorkspaceRoot().getLocation().removeLastSegments(1).append("external.jar").toOSString();
+ try {
+ IJavaProject p = createJavaProject("P");
+ Util.writeToFile("", externalJarPath);
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newLibraryEntry(new Path("../../external.jar"), null, null)});
+ assertElementDescendants(
+ "Unexpected project content",
+ "P\n" +
+ " "+ getExternalPath() + "external.jar",
+ p
+ );
+ } finally {
+ deleteResource(new File(externalJarPath));
+ deleteProject("P");
+ }
+}
+
+/*
+ * Ensures that one can point at an external library using a ".." path
+ */
+public void testDotDotLibraryEntry3() throws Exception {
+ String externalJarPath = getWorkspaceRoot().getLocation().removeLastSegments(1).append("external.jar").toOSString();
+ try {
+ IJavaProject p = createJavaProject("P");
+ Util.writeToFile("", externalJarPath);
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newLibraryEntry(new Path("src/../../../external.jar"), null, null)});
+ assertElementDescendants(
+ "Unexpected project content",
+ "P\n" +
+ " "+ getExternalPath() + "external.jar",
+ p
+ );
+ } finally {
+ deleteResource(new File(externalJarPath));
+ deleteProject("P");
+ }
+}
+
+/*
+ * Ensures that one can point at an internal library using a ".." path
+ */
+public void testDotDotLibraryEntry4() throws Exception {
+ try {
+ createProject("P1");
+ createFile("/P1/internal.jar", "");
+ IJavaProject p = createJavaProject("P2");
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newLibraryEntry(new Path("../P1/internal.jar"), null, null)});
+ assertElementDescendants(
+ "Unexpected project content",
+ "P2\n" +
+ " /P1/internal.jar",
+ p
+ );
+ } finally {
+ deleteProject("P1");
+ deleteProject("P2");
+ }
+}
+
+/*
+ * Ensures that no markers are created if one can point at an existing external library using a ".." path
+ */
+public void testDotDotLibraryEntry5() throws Exception {
+ String externalJarPath = getWorkspaceRoot().getLocation().append("external.jar").toOSString();
+ try {
+ IJavaProject p = createJavaProject("P");
+ Util.writeToFile("", externalJarPath);
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newLibraryEntry(new Path("../external.jar"), null, null)});
+ assertMarkers(
+ "Unexpected markers",
+ "",
+ p);
+ } finally {
+ deleteResource(new File(externalJarPath));
+ deleteProject("P");
+ }
+}
+
+/*
+ * Ensures that a marker is created if one can point at a non-existing external library using a ".." path
+ */
+public void testDotDotLibraryEntry6() throws Exception {
+ try {
+ IJavaProject p = createJavaProject("P");
+ setClasspath(p, new IClasspathEntry[] {JavaCore.newLibraryEntry(new Path("../external.jar"), null, null)});
+ assertMarkers(
+ "Unexpected markers",
+ "Project \'P\' is missing required library: \'"+ getWorkspacePath() + "external.jar\'",
+ p);
+ } finally {
+ deleteProject("P");
+ }
+}
+
+/*
+ * Ensures that one can point at an external library using a ".." path by editing the .classpath file
+ */
+public void testDotDotLibraryEntry7() throws Exception {
+ String externalJarPath = getWorkspaceRoot().getLocation().append("external.jar").toOSString();
+ try {
+ IJavaProject p = createJavaProject("P");
+ Util.writeToFile("", externalJarPath);
+ editFile(
+ "/P/.classpath",
+ "\n" +
+ "