Issue #59311: utilities to interconvert URL, File, ClassPath, (path) String. diff --git a/api.java/apichanges.xml b/api.java/apichanges.xml --- a/api.java/apichanges.xml +++ b/api.java/apichanges.xml @@ -73,6 +73,26 @@ made subject to such option by the copyr + + + + Interconversions with string-format classpaths + + + + + +

+ ClassPath.toString(PathConversionMode) and + ClassPathSupport.createClassPath(String) + can be used to easily convert between traditional string classpaths + and NetBeans' internal representation. +

+
+ + + +
diff --git a/api.java/manifest.mf b/api.java/manifest.mf --- a/api.java/manifest.mf +++ b/api.java/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.java/1 -OpenIDE-Module-Specification-Version: 1.14 +OpenIDE-Module-Specification-Version: 1.15 OpenIDE-Module-Localizing-Bundle: org/netbeans/api/java/classpath/Bundle.properties AutoUpdate-Show-In-Client: false diff --git a/api.java/nbproject/project.xml b/api.java/nbproject/project.xml --- a/api.java/nbproject/project.xml +++ b/api.java/nbproject/project.xml @@ -47,12 +47,26 @@ made subject to such option by the copyr org.netbeans.api.java + org.openide.execution + + + + 1.2 + + + org.openide.filesystems - 7.7 + 7.8 + + + org.openide.io + + + org.openide.util @@ -62,37 +76,23 @@ made subject to such option by the copyr 7.8 - - org.openide.execution - - - - 1.2 - - - - org.openide.io - - - - - - - unit - - org.openide.util - - - org.openide.modules - - - org.netbeans.modules.masterfs - - - - qa-functional - + + + unit + + org.openide.util + + + org.openide.modules + + + org.netbeans.modules.masterfs + + + + qa-functional + org.netbeans.api.java.classpath diff --git a/api.java/src/org/netbeans/api/java/classpath/ClassPath.java b/api.java/src/org/netbeans/api/java/classpath/ClassPath.java --- a/api.java/src/org/netbeans/api/java/classpath/ClassPath.java +++ b/api.java/src/org/netbeans/api/java/classpath/ClassPath.java @@ -58,7 +58,6 @@ import java.util.Collection; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -76,7 +75,6 @@ import org.openide.filesystems.FileObjec import org.openide.filesystems.FileObject; import org.openide.filesystems.FileRenameEvent; import org.openide.filesystems.FileStateInvalidException; -import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileUtil; import org.openide.filesystems.URLMapper; import org.openide.util.Exceptions; @@ -265,9 +263,9 @@ public final class ClassPath { private FileObject[] createRoots (final List entries) { List l = new ArrayList (); for (Entry entry : entries) { - RootsListener rootsListener = this.getRootsListener(); - if (rootsListener != null) { - rootsListener.addRoot (entry.getURL()); + RootsListener rL = this.getRootsListener(); + if (rL != null) { + rL.addRoot (entry.getURL()); } FileObject fo = entry.getRoot(); if (fo != null) { @@ -560,8 +558,76 @@ public final class ClassPath { propSupport.firePropertyChange(event); } - public String toString() { - return "ClassPath" + entries(); // NOI18N + /** + * Policy for handling path items which cannot be converted into the desired format. + * @see #toString(ClassPath.PathConversionMode) + * @since org.netbeans.api.java/1 1.15 + */ + public enum PathConversionMode { + /** + * Drop entry silently. + */ + SKIP, + /** + * Entry is dropped but a warning is logged. + */ + WARN, + /** + * {@link #toString(ClassPath.PathConversionMode)} will fail with an {@link IllegalArgumentException}. + */ + FAIL, + /** + * The entry is simply displayed as a URL. + * Useful for debug logging or unit tests. + * Used by {@link ClassPath#toString}. + */ + PRINT, + } + + /** + * Render this classpath in the conventional format used by the Java launcher. + * @param conversionMode policy for converting unusual entries + * @return a conventionally-formatted representation of the classpath + * @since org.netbeans.api.java/1 1.15 + * @see File#pathSeparator + * @see FileUtil#archiveOrDirForURL + * @see ClassPathSupport#createClassPath(String) + */ + public String toString(PathConversionMode conversionMode) { + StringBuilder b = new StringBuilder(); + for (Entry e : entries()) { + URL u = e.getURL(); + File f = FileUtil.archiveOrDirForURL(u); + if (f != null) { + if (b.length() > 0) { + b.append(File.pathSeparatorChar); + } + b.append(f.getAbsolutePath()); + } else { + switch (conversionMode) { + case SKIP: + break; + case PRINT: + if (b.length() > 0) { + b.append(File.pathSeparatorChar); + } + b.append(u); + break; + case WARN: + LOG.warning("Encountered untranslatable classpath entry: " + u); + break; + case FAIL: + throw new IllegalArgumentException("Encountered untranslatable classpath entry: " + u); // NOI18N + default: + assert false : conversionMode; + } + } + } + return b.toString(); + } + + public @Override String toString() { + return toString(PathConversionMode.PRINT); } /** @@ -623,8 +689,8 @@ public final class ClassPath { * folder. */ public boolean isValid() { - FileObject root = getRoot(); - return root != null && root.isValid(); + FileObject r = getRoot(); + return r != null && r.isValid(); } /** @@ -688,13 +754,13 @@ public final class ClassPath { * @since org.netbeans.api.java/1 1.13 */ public boolean includes(FileObject file) { - FileObject root = getRoot(); - if (root == null) { + FileObject r = getRoot(); + if (r == null) { throw new IllegalArgumentException("no root in " + url); } - String path = FileUtil.getRelativePath(root, file); + String path = FileUtil.getRelativePath(r, file); if (path == null) { - throw new IllegalArgumentException(file + " not in " + root); + throw new IllegalArgumentException(file + " not in " + r); } if (file.isFolder()) { path += "/"; // NOI18N @@ -709,7 +775,7 @@ public final class ClassPath { this.filter = filter; } - public String toString() { + public @Override String toString() { return "Entry[" + url + "]"; // NOI18N } diff --git a/api.java/src/org/netbeans/spi/java/classpath/support/ClassPathSupport.java b/api.java/src/org/netbeans/spi/java/classpath/support/ClassPathSupport.java --- a/api.java/src/org/netbeans/spi/java/classpath/support/ClassPathSupport.java +++ b/api.java/src/org/netbeans/spi/java/classpath/support/ClassPathSupport.java @@ -40,6 +40,7 @@ */ package org.netbeans.spi.java.classpath.support; +import java.io.File; import org.netbeans.spi.java.classpath.PathResourceImplementation; import org.netbeans.spi.java.classpath.ClassPathImplementation; import org.netbeans.spi.java.classpath.ClassPathFactory; @@ -161,6 +162,27 @@ public class ClassPathSupport { return createClassPath(l); } + /** + * Convenience method to create a classpath object from a conventional string representation. + * @param jvmPath a JVM-style classpath (folder or archive paths separated by {@link File#pathSeparator}) + * @return a corresponding classpath object + * @throws IllegalArgumentException in case a path entry looks to be invalid + * @since org.netbeans.api.java/1 1.15 + * @see FileUtil#urlForArchiveOrDir + * @see ClassPath#toJVMPath + */ + public static ClassPath createClassPath(String jvmPath) throws IllegalArgumentException { + List l = new ArrayList(); + for (String piece : jvmPath.split(File.pathSeparator)) { + File f = FileUtil.normalizeFile(new File(piece)); + URL u = FileUtil.urlForArchiveOrDir(f); + if (u == null) { + throw new IllegalArgumentException("Path entry looks to be invalid: " + piece); // NOI18N + } + l.add(createResource(u)); + } + return createClassPath(l); + } /** * Creates read only proxy ClassPathImplementation for given delegates. diff --git a/api.java/test/unit/src/org/netbeans/api/java/classpath/ClassPathTest.java b/api.java/test/unit/src/org/netbeans/api/java/classpath/ClassPathTest.java --- a/api.java/test/unit/src/org/netbeans/api/java/classpath/ClassPathTest.java +++ b/api.java/test/unit/src/org/netbeans/api/java/classpath/ClassPathTest.java @@ -62,7 +62,9 @@ import java.util.StringTokenizer; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.jar.JarOutputStream; +import java.util.logging.Level; import java.util.zip.ZipEntry; +import org.netbeans.junit.Log; import org.netbeans.junit.NbTestCase; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.netbeans.spi.java.classpath.ClassPathImplementation; @@ -78,7 +80,7 @@ public class ClassPathTest extends NbTes super(testName); } - protected void setUp() throws Exception { + protected @Override void setUp() throws Exception { super.setUp(); clearWorkDir(); } @@ -192,6 +194,7 @@ public class ClassPathTest extends NbTes * ClassPath.entries () and classpath SPI. */ public void testListening() throws Exception { + // XXX unreliable, would be improved by usage of TestFileUtils methods: File root_1 = new File (getBaseDir(),"root_1"); root_1.mkdir(); @@ -668,5 +671,31 @@ public class ClassPathTest extends NbTes return !fo.getName().contains("$"); //NOI18N } - + + public void testJVMPathConversion() throws Exception { + String root = getWorkDir().toURI().toString(); + ClassPath cp = ClassPathSupport.createClassPath( + new URL(root + "folder/"), + new URL("jar:" + root + "file.zip!/"), + new URL("jar:" + root + "file.zip!/subdir/")); + assertEquals(massagePath("/folder:/file.zip"), cp.toString(ClassPath.PathConversionMode.SKIP)); + assertEquals(massagePath("/folder:/file.zip:") + "jar:" + root + "file.zip!/subdir/", cp.toString(ClassPath.PathConversionMode.PRINT)); + try { + cp.toString(ClassPath.PathConversionMode.FAIL); + fail(); + } catch (IllegalArgumentException x) {/* OK */} + CharSequence warnings = Log.enable(ClassPath.class.getName(), Level.WARNING); + assertEquals(massagePath("/folder:/file.zip"), cp.toString(ClassPath.PathConversionMode.WARN)); + assertTrue(warnings.toString(), warnings.toString().contains("subdir")); + + cp = ClassPathSupport.createClassPath( + new URL(root + "folder/"), + new URL("jar:" + root + "file.zip!/")); + assertEquals(cp.toString(), ClassPathSupport.createClassPath(cp.toString()).toString()); + // XXX could also test IAE (tricky - need to have a URLMapper in Lookup, etc.) + } + private String massagePath(String path) throws Exception { + return path.replace('/', File.separatorChar).replace(':', File.pathSeparatorChar).replace("", getWorkDir().getAbsolutePath()); + } + } diff --git a/apisupport.project/nbproject/project.xml b/apisupport.project/nbproject/project.xml --- a/apisupport.project/nbproject/project.xml +++ b/apisupport.project/nbproject/project.xml @@ -70,6 +70,7 @@ made subject to such option by the copyr 1 + 1.15 @@ -239,7 +240,7 @@ made subject to such option by the copyr - 7.0 + 7.8 diff --git a/apisupport.project/src/org/netbeans/modules/apisupport/project/Evaluator.java b/apisupport.project/src/org/netbeans/modules/apisupport/project/Evaluator.java --- a/apisupport.project/src/org/netbeans/modules/apisupport/project/Evaluator.java +++ b/apisupport.project/src/org/netbeans/modules/apisupport/project/Evaluator.java @@ -47,8 +47,6 @@ import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; -import java.net.URI; -import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -494,27 +492,10 @@ final class Evaluator implements Propert for (JavaPlatform platform : JavaPlatformManager.getDefault().getInstalledPlatforms()) { if (new HashSet(platform.getInstallFolders()).equals(Collections.singleton(homeFO))) { // Matching JDK is registered, so look up its real bootcp. - StringBuffer bootcpSB = new StringBuffer(); ClassPath boot = platform.getBootstrapLibraries(); boot.removePropertyChangeListener(weakListener); boot.addPropertyChangeListener(weakListener); - for (ClassPath.Entry entry : boot.entries()) { - URL u = entry.getURL(); - if (u.toExternalForm().endsWith("!/")) { // NOI18N - URL nested = FileUtil.getArchiveFile(u); - if (nested != null) { - u = nested; - } - } - if ("file".equals(u.getProtocol())) { // NOI18N - File f = new File(URI.create(u.toExternalForm())); - if (bootcpSB.length() > 0) { - bootcpSB.append(File.pathSeparatorChar); - } - bootcpSB.append(f.getAbsolutePath()); - } - } - bootcp = bootcpSB.toString(); + bootcp = boot.toString(ClassPath.PathConversionMode.WARN); break; } } diff --git a/apisupport.project/src/org/netbeans/modules/apisupport/project/Util.java b/apisupport.project/src/org/netbeans/modules/apisupport/project/Util.java --- a/apisupport.project/src/org/netbeans/modules/apisupport/project/Util.java +++ b/apisupport.project/src/org/netbeans/modules/apisupport/project/Util.java @@ -224,57 +224,25 @@ public final class Util { return to; } - // CANDIDATES FOR FileUtil (#59311): - /** - * Creates a URL for a directory on disk. - * Works correctly even if the directory does not currently exist. + * @deprecated Use FileUtil#urlForArchiveOrDir instead. */ public static URL urlForDir(File dir) { - try { - URL u = FileUtil.normalizeFile(dir).toURI().toURL(); - String s = u.toExternalForm(); - if (s.endsWith("/")) { // NOI18N - return u; - } else { - return new URL(s + "/"); // NOI18N - } - } catch (MalformedURLException e) { - throw new AssertionError(e); - } + return FileUtil.urlForArchiveOrDir(dir); } /** - * Creates a URL for the root of a JAR on disk. + * @deprecated Use FileUtil#urlForArchiveOrDir instead. */ public static URL urlForJar(File jar) { - try { - return FileUtil.getArchiveRoot(FileUtil.normalizeFile(jar).toURI().toURL()); - } catch (MalformedURLException e) { - throw new AssertionError(e); - } + return FileUtil.urlForArchiveOrDir(jar); } /** - * Creates a URL for a directory on disk or the root of a JAR. - * Works correctly whether or not the directory or JAR currently exists. - * Detects whether the file is supposed to be a directory or a JAR. + * @deprecated Use FileUtil#urlForArchiveOrDir instead. */ public static URL urlForDirOrJar(File location) { - try { - URL u = FileUtil.normalizeFile(location).toURI().toURL(); - if (FileUtil.isArchiveFile(u)) { - u = FileUtil.getArchiveRoot(u); - } else { - String us = u.toExternalForm(); - if (!us.endsWith("/")) { // NOI18N - u = new URL(us + "/"); // NOI18N - } - } - return u; - } catch (MalformedURLException e) { - throw new AssertionError(e); - } + return FileUtil.urlForArchiveOrDir(location); } /** diff --git a/apisupport.project/src/org/netbeans/modules/apisupport/project/ui/platform/PlatformComponentFactory.java b/apisupport.project/src/org/netbeans/modules/apisupport/project/ui/platform/PlatformComponentFactory.java --- a/apisupport.project/src/org/netbeans/modules/apisupport/project/ui/platform/PlatformComponentFactory.java +++ b/apisupport.project/src/org/netbeans/modules/apisupport/project/ui/platform/PlatformComponentFactory.java @@ -45,7 +45,6 @@ import java.awt.Component; import java.awt.Component; import java.io.File; import java.io.IOException; -import java.net.URI; import java.net.URL; import java.text.Collator; import java.util.Arrays; @@ -501,15 +500,8 @@ public final class PlatformComponentFact public @Override Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { URL u = (URL) value; - String text = u.toExternalForm(); - if (u.getProtocol().equals("file")) { // NOI18N - text = new File(URI.create(u.toExternalForm())).getAbsolutePath(); - } else if (u.getProtocol().equals("jar")) { // NOI18N - URL baseU = FileUtil.getArchiveFile(u); - if (u.equals(FileUtil.getArchiveRoot(baseU)) && baseU.getProtocol().equals("file")) { // NOI18N - text = new File(URI.create(baseU.toExternalForm())).getAbsolutePath(); - } - } + File f = FileUtil.archiveOrDirForURL(u); + String text = f != null ? f.getAbsolutePath() : u.toString(); return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus); } } diff --git a/apisupport.project/src/org/netbeans/modules/apisupport/project/universe/NbPlatform.java b/apisupport.project/src/org/netbeans/modules/apisupport/project/universe/NbPlatform.java --- a/apisupport.project/src/org/netbeans/modules/apisupport/project/universe/NbPlatform.java +++ b/apisupport.project/src/org/netbeans/modules/apisupport/project/universe/NbPlatform.java @@ -65,9 +65,11 @@ import java.util.TreeSet; import java.util.TreeSet; import java.util.jar.JarFile; import java.util.zip.ZipEntry; +import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.project.ProjectManager; import org.netbeans.modules.apisupport.project.ManifestManager; import org.netbeans.modules.apisupport.project.Util; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.netbeans.spi.project.support.ant.EditableProperties; import org.netbeans.spi.project.support.ant.PropertyUtils; import org.openide.ErrorManager; @@ -669,22 +671,7 @@ public final class NbPlatform { } static String urlsToAntPath(final URL[] urls) { - StringBuffer path = new StringBuffer(); - for (int i = 0; i < urls.length; i++) { - if (urls[i].getProtocol().equals("jar")) { // NOI18N - path.append(urlToAntPath(FileUtil.getArchiveFile(urls[i]))); - } else { - path.append(urlToAntPath(urls[i])); - } - if (i != urls.length - 1) { - path.append(':'); // NOI18N - } - } - return path.toString(); - } - - private static String urlToAntPath(final URL url) { - return new File(URI.create(url.toExternalForm())).getAbsolutePath(); + return ClassPathSupport.createClassPath(urls).toString(ClassPath.PathConversionMode.WARN); } private void putGlobalProperty(final String key, final String value) throws IOException { diff --git a/java.api.common/nbproject/project.xml b/java.api.common/nbproject/project.xml --- a/java.api.common/nbproject/project.xml +++ b/java.api.common/nbproject/project.xml @@ -89,7 +89,7 @@ - 7.4 + 7.8 diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/classpath/j2ee/BaseClassPathSupport.java b/java.api.common/src/org/netbeans/modules/java/api/common/classpath/j2ee/BaseClassPathSupport.java --- a/java.api.common/src/org/netbeans/modules/java/api/common/classpath/j2ee/BaseClassPathSupport.java +++ b/java.api.common/src/org/netbeans/modules/java/api/common/classpath/j2ee/BaseClassPathSupport.java @@ -107,11 +107,7 @@ abstract class BaseClassPathSupport roots = item.getLibrary().getContent("classpath"); // NOI18N for (URL rootUrl : roots) { - FileObject root = URLMapper.findFileObject(rootUrl); - if ("jar".equals(rootUrl.getProtocol())) { // NOI18N - root = FileUtil.getArchiveFile(root); - } - File f = FileUtil.toFile(root); + File f = FileUtil.archiveOrDirForURL(rootUrl); if (f != null) { if (f.isFile()) { files.add(f); diff --git a/java.j2seproject/nbproject/project.xml b/java.j2seproject/nbproject/project.xml --- a/java.j2seproject/nbproject/project.xml +++ b/java.j2seproject/nbproject/project.xml @@ -247,7 +247,7 @@ made subject to such option by the copyr - 6.2 + 7.8 diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProjectUtil.java b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProjectUtil.java --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProjectUtil.java +++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProjectUtil.java @@ -132,11 +132,9 @@ public class J2SEProjectUtil { * @throws MalformedURLException if the URL cannot be created */ public static URL getRootURL (File root, String offset) throws MalformedURLException { - URL url = root.toURI().toURL(); - if (FileUtil.isArchiveFile(url)) { - url = FileUtil.getArchiveRoot(url); - } else if (!root.exists()) { - url = new URL(url.toExternalForm() + "/"); // NOI18N + URL url = FileUtil.urlForArchiveOrDir(root); + if (url == null) { + throw new IllegalArgumentException(root.getAbsolutePath()); } if (offset != null) { assert offset.endsWith("/"); //NOI18N diff --git a/java.project/nbproject/project.xml b/java.project/nbproject/project.xml --- a/java.project/nbproject/project.xml +++ b/java.project/nbproject/project.xml @@ -167,7 +167,7 @@ made subject to such option by the copyr - 6.2 + 7.8 diff --git a/java.project/src/org/netbeans/modules/java/project/ExtraProjectJavadocForBinaryQueryImpl.java b/java.project/src/org/netbeans/modules/java/project/ExtraProjectJavadocForBinaryQueryImpl.java --- a/java.project/src/org/netbeans/modules/java/project/ExtraProjectJavadocForBinaryQueryImpl.java +++ b/java.project/src/org/netbeans/modules/java/project/ExtraProjectJavadocForBinaryQueryImpl.java @@ -146,18 +146,11 @@ public final class ExtraProjectJavadocFo String val = entry.getKey().substring(REF_START.length()); String sourceKey = JAVADOC_START + val; String source = props.get(sourceKey); - try { - File bin = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), entry.getValue()); - URL binURL = bin.toURI().toURL(); - if (FileUtil.isArchiveFile(binURL)) { - binURL = FileUtil.getArchiveRoot(binURL); - } - if (source != null) { - File src = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), source); - result.put(binURL, src.toURI()); - } - } catch (MalformedURLException ex) { - Exceptions.printStackTrace(ex); + File bin = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), entry.getValue()); + URL binURL = FileUtil.urlForArchiveOrDir(bin); + if (source != null && binURL != null) { + File src = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), source); + result.put(binURL, src.toURI()); } } } diff --git a/java.project/src/org/netbeans/modules/java/project/ExtraProjectSourceForBinaryQueryImpl.java b/java.project/src/org/netbeans/modules/java/project/ExtraProjectSourceForBinaryQueryImpl.java --- a/java.project/src/org/netbeans/modules/java/project/ExtraProjectSourceForBinaryQueryImpl.java +++ b/java.project/src/org/netbeans/modules/java/project/ExtraProjectSourceForBinaryQueryImpl.java @@ -150,17 +150,10 @@ public final class ExtraProjectSourceFor String sourceKey = SOURCE_START + val; String source = props.get(sourceKey); File bin = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), entry.getValue()); - try { - URL binURL = bin.toURI().toURL(); - if (FileUtil.isArchiveFile(binURL)) { - binURL = FileUtil.getArchiveRoot(binURL); - } - if (source != null) { - File src = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), source); - result.put(binURL, src.toURI()); - } - } catch (MalformedURLException ex) { - Exceptions.printStackTrace(ex); + URL binURL = FileUtil.urlForArchiveOrDir(bin); + if (source != null && binURL != null) { + File src = PropertyUtils.resolveFile(FileUtil.toFile(helper.getProjectDirectory()), source); + result.put(binURL, src.toURI()); } } } diff --git a/java.project/src/org/netbeans/modules/java/project/JavaAntLogger.java b/java.project/src/org/netbeans/modules/java/project/JavaAntLogger.java --- a/java.project/src/org/netbeans/modules/java/project/JavaAntLogger.java +++ b/java.project/src/org/netbeans/modules/java/project/JavaAntLogger.java @@ -294,17 +294,9 @@ public final class JavaAntLogger extends while (tok.hasMoreTokens()) { String binrootS = tok.nextToken(); File f = FileUtil.normalizeFile(new File(binrootS)); - URL binroot; - try { - binroot = f.toURI().toURL(); - } catch (MalformedURLException e) { - throw new AssertionError(e); - } - if (FileUtil.isArchiveFile(binroot)) { - URL root = FileUtil.getArchiveRoot(binroot); - if (root != null) { - binroot = root; - } + URL binroot = FileUtil.urlForArchiveOrDir(f); + if (binroot == null) { + continue; } FileObject[] someRoots = SourceForBinaryQuery.findSourceRoots(binroot).getRoots(); data.classpathSourceRoots.addAll(Arrays.asList(someRoots)); diff --git a/java.project/src/org/netbeans/spi/java/project/classpath/support/ProjectClassPathImplementation.java b/java.project/src/org/netbeans/spi/java/project/classpath/support/ProjectClassPathImplementation.java --- a/java.project/src/org/netbeans/spi/java/project/classpath/support/ProjectClassPathImplementation.java +++ b/java.project/src/org/netbeans/spi/java/project/classpath/support/ProjectClassPathImplementation.java @@ -45,20 +45,19 @@ import java.beans.PropertyChangeListener import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; import org.netbeans.api.project.ProjectManager; import org.netbeans.spi.java.classpath.ClassPathImplementation; import org.netbeans.spi.java.classpath.PathResourceImplementation; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.netbeans.spi.project.support.ant.PropertyEvaluator; import org.netbeans.spi.project.support.ant.PropertyUtils; -import org.openide.ErrorManager; import org.openide.filesystems.FileUtil; import org.openide.util.WeakListeners; @@ -137,25 +136,11 @@ final class ProjectClassPathImplementati if (prop != null) { for (String piece : PropertyUtils.tokenizePath(prop)) { File f = PropertyUtils.resolveFile(this.projectFolder, piece); - try { - URL entry = f.toURI().toURL(); - if (FileUtil.isArchiveFile(entry) || (f.isFile() && f.length()<4)) { //XXX: Not yet closed archive file - entry = FileUtil.getArchiveRoot(entry); - } else if (!f.exists()) { - // if file does not exist (e.g. build/classes folder - // was not created yet) then corresponding File will - // not be ended with slash. Fix that. - assert !entry.toExternalForm().endsWith("/") : f; // NOI18N - entry = new URL(entry.toExternalForm() + "/"); // NOI18N - } - else if (f.isFile()) { - ErrorManager.getDefault().log(ErrorManager.ERROR,"ProjectClassPathImplementation: file: "+f.getAbsolutePath() - +" is not a valid archive file."); //NOI18N - continue; - } + URL entry = FileUtil.urlForArchiveOrDir(f); + if (entry != null) { result.add(ClassPathSupport.createResource(entry)); - } catch (MalformedURLException mue) { - assert false : mue; + } else { + Logger.getLogger(ProjectClassPathImplementation.class.getName()).warning(f + " does not look like a valid archive file"); } } } diff --git a/openide.filesystems/apichanges.xml b/openide.filesystems/apichanges.xml --- a/openide.filesystems/apichanges.xml +++ b/openide.filesystems/apichanges.xml @@ -46,6 +46,22 @@ made subject to such option by the copyr Filesystems API + + + Added methods to interconvert URLs and traditional path entries + + + + + +

+ Added methods urlForArchiveOrDir and archiveOrDirForURL + to FileUtil to make it easier to work with classpaths. +

+
+ + +
Add methods: addFileChangeListener, removeFileChangeListener, refreshAll diff --git a/openide.filesystems/manifest.mf b/openide.filesystems/manifest.mf --- a/openide.filesystems/manifest.mf +++ b/openide.filesystems/manifest.mf @@ -1,5 +1,5 @@ Manifest-Version: 1.0 Manifest-Version: 1.0 OpenIDE-Module: org.openide.filesystems -OpenIDE-Module-Specification-Version: 7.7 +OpenIDE-Module-Specification-Version: 7.8 OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties diff --git a/openide.filesystems/src/org/openide/filesystems/FileUtil.java b/openide.filesystems/src/org/openide/filesystems/FileUtil.java --- a/openide.filesystems/src/org/openide/filesystems/FileUtil.java +++ b/openide.filesystems/src/org/openide/filesystems/FileUtil.java @@ -1683,6 +1683,55 @@ public final class FileUtil extends Obje } /** + * Convert a file such as would be shown in a classpath entry into a proper folder URL. + * If the file looks to represent a directory, a file URL will be created. + * If it looks to represent a ZIP archive, a jar URL will be created. + * @param entry a file or directory name + * @return an appropriate classpath URL which will always end in a slash (/), + * or null for an existing file which does not look like a valid archive + * @since org.openide.filesystems 7.8 + */ + public static URL urlForArchiveOrDir(File entry) { + try { + URL u = entry.toURI().toURL(); + if (isArchiveFile(u) || entry.isFile() && entry.length() < 4) { + return getArchiveRoot(u); + } else if (entry.isDirectory()) { + return u; + } else if (!entry.exists()) { + assert !u.toString().endsWith("/") : u; + return new URL(u + "/"); // NOI18N + } else { + return null; + } + } catch (MalformedURLException x) { + assert false : x; + return null; + } + } + + /** + * Convert a classpath-type URL to a corresponding file. + * If it is a jar URL representing the root folder of a local disk archive, + * that archive file will be returned. + * If it is a file URL representing a local disk folder, + * that folder will be returned. + * @param entry a classpath entry or similar URL + * @return a corresponding file, or null for e.g. a network URL or non-root JAR folder entry + * @since org.openide.filesystems 7.8 + */ + public static File archiveOrDirForURL(URL entry) { + String u = entry.toString(); + if (u.startsWith("jar:file:") && u.endsWith("!/")) { // NOI18N + return new File(URI.create(u.substring(4, u.length() - 2))); + } else if (u.startsWith("file:")) { // NOI18N + return new File(URI.create(u)); + } else { + return null; + } + } + + /** * Make sure that a JFileChooser does not traverse symlinks on Unix. * @param chooser a file chooser * @param currentDirectory if not null, a file to set as the current directory diff --git a/openide.filesystems/src/org/openide/filesystems/URLMapper.java b/openide.filesystems/src/org/openide/filesystems/URLMapper.java --- a/openide.filesystems/src/org/openide/filesystems/URLMapper.java +++ b/openide.filesystems/src/org/openide/filesystems/URLMapper.java @@ -100,6 +100,15 @@ public abstract class URLMapper { static { DefaultURLMapperProxy.setDefault(new DefaultURLMapper()); + reset(); + } + + /** Cache of all available URLMapper instances. */ + private static List cache; + + /** Reset cache, for use from unit tests. */ + static void reset() { + cache = null; result = Lookup.getDefault().lookupResult(URLMapper.class); result.addLookupListener( new LookupListener() { @@ -111,12 +120,6 @@ public abstract class URLMapper { } ); } - - /** Basic impl. for JarFileSystem, LocalFileSystem, MultiFileSystem */ - private static URLMapper defMapper; - - /** Cache of all available URLMapper instances. */ - private static List cache; /** Find a good URL for this file object which works according to type: *
    diff --git a/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java b/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java --- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java +++ b/openide.filesystems/test/unit/src/org/openide/filesystems/FileUtilTest.java @@ -43,9 +43,10 @@ package org.openide.filesystems; import java.io.File; import java.net.URL; -import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.test.TestFileUtils; import org.openide.util.Utilities; +import org.openide.util.test.MockLookup; /** * @author Jesse Glick @@ -64,23 +65,82 @@ public class FileUtilTest extends NbTest assertTrue(root.isDirectory()); LocalFileSystem lfs = new LocalFileSystem(); lfs.setRootDirectory(root); - LFS_ROOT = lfs.getRoot(); - MockServices.setServices(TestUM.class); + final FileObject LFS_ROOT = lfs.getRoot(); + MockLookup.setInstances(new URLMapper() { + public URL getURL(FileObject fo, int type) { + return null; + } + public FileObject[] getFileObjects(URL url) { + if (url.toExternalForm().equals("file:/")) { + return new FileObject[] {LFS_ROOT}; + } else { + return null; + } + } + }); + URLMapper.reset(); assertEquals(LFS_ROOT, FileUtil.toFileObject(root)); } - private static FileObject LFS_ROOT; - public static class TestUM extends URLMapper { - public URL getURL(FileObject fo, int type) { - throw new UnsupportedOperationException(); - } - public FileObject[] getFileObjects(URL url) { - if (url.toExternalForm().equals("file:/")) { - return new FileObject[] {LFS_ROOT}; - } else { + public void testArchiveConversion() throws Exception { + final LocalFileSystem lfs = new LocalFileSystem(); + clearWorkDir(); + lfs.setRootDirectory(getWorkDir()); + MockLookup.setInstances(new URLMapper() { + String rootURL = lfs.getRoot().getURL().toString(); + @Override + public FileObject[] getFileObjects(URL url) { + String u = url.toString(); + FileObject f = null; + if (u.startsWith(rootURL)) { + f = lfs.findResource(u.substring(rootURL.length())); + } + return f != null ? new FileObject[] {f} : null; + } + @Override + public URL getURL(FileObject fo, int type) { return null; } - } + }); + URLMapper.reset(); + + TestFileUtils.writeFile(lfs.getRoot(), "README", "A random file with some stuff in it."); + assertCorrectURL("README", null, null); // not an archive + TestFileUtils.writeFile(lfs.getRoot(), "README.txt", "A random file with some stuff in it."); + assertCorrectURL("README.txt", null, null); // not an archive either + TestFileUtils.writeFile(lfs.getRoot(), "empty.zip", ""); + assertCorrectURL("empty.zip", "jar:", "empty.zip!/"); + TestFileUtils.writeZipFile(lfs.getRoot(), "normal.zip", "something:text inside a ZIP entry"); + assertCorrectURL("normal.zip", "jar:", "normal.zip!/"); + assertCorrectURL("nonexistent.zip", "jar:", "nonexistent.zip!/"); + lfs.getRoot().createFolder("folder"); + assertCorrectURL("folder", "", "folder/"); + lfs.getRoot().createFolder("some.folder"); + assertCorrectURL("some.folder", "", "some.folder/"); + assertCorrectURL("nonexistent", "", "nonexistent/"); + assertCorrectURL("non existent.zip", "jar:", "non%20existent.zip!/"); + assertCorrectURL("non existent", "", "non%20existent/"); + + assertCorrectFile("folder", "", "folder/"); + assertCorrectFile("stuff.zip", "jar:", "stuff.zip!/"); + assertCorrectFile(null, "jar:", "stuff.zip!/subentry/"); + assertCorrectFile(null, "http:", ""); + // Impossible to even construct such a URL: assertCorrectFolder("stuff.zip", "jar:", "stuff.zip"); + assertCorrectFile("stuff.zip", "", "stuff.zip"); + assertCorrectFile("folder", "", "folder"); + assertCorrectFile("fol der", "", "fol%20der/"); + assertCorrectFile("stu ff.zip", "jar:", "stu%20ff.zip!/"); + assertCorrectFile("stu ff.zip", "", "stu%20ff.zip"); + assertCorrectFile("fol der", "", "fol%20der"); + } + private void assertCorrectURL(String filename, String expectedURLPrefix, String expectedURLSuffix) throws Exception { + File d = getWorkDir(); + assertEquals(expectedURLSuffix == null ? null : new URL(expectedURLPrefix + d.toURI() + expectedURLSuffix), + FileUtil.urlForArchiveOrDir(new File(d, filename))); + } + private void assertCorrectFile(String expectedFilename, String urlPrefix, String urlSuffix) throws Exception { + assertEquals(expectedFilename == null ? null : new File(getWorkDir(), expectedFilename), + FileUtil.archiveOrDirForURL(new URL(urlPrefix + getWorkDir().toURI() + urlSuffix))); } } diff --git a/project.ant/nbproject/project.xml b/project.ant/nbproject/project.xml --- a/project.ant/nbproject/project.xml +++ b/project.ant/nbproject/project.xml @@ -119,7 +119,7 @@ made subject to such option by the copyr - 6.2 + 7.8 diff --git a/project.ant/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java b/project.ant/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java --- a/project.ant/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java +++ b/project.ant/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java @@ -673,12 +673,8 @@ public final class GeneratedFilesHelper /** Find the time the file this URL represents was last modified xor its size, if possible. */ private static long checkFootprint(URL u) { - URL nested = FileUtil.getArchiveFile(u); - if (nested != null) { - u = nested; - } - if (u.getProtocol().equals("file")) { // NOI18N - File f = new File(URI.create(u.toExternalForm())); + File f = FileUtil.archiveOrDirForURL(u); + if (f != null) { return f.lastModified() ^ f.length(); } else { return 0L;