--- a/javahelp/nbproject/project.xml +++ a/javahelp/nbproject/project.xml @@ -79,7 +79,7 @@ - 7.50 + 7.51 --- a/javahelp/src/org/netbeans/modules/javahelp/HelpSetRegistrationProcessor.java +++ a/javahelp/src/org/netbeans/modules/javahelp/HelpSetRegistrationProcessor.java @@ -45,7 +45,6 @@ import com.sun.java.help.search.Indexer; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -68,6 +67,7 @@ import javax.tools.StandardLocation; import org.netbeans.api.javahelp.HelpSetRegistration; import org.openide.filesystems.FileUtil; +import org.openide.filesystems.annotations.LayerBuilder; import org.openide.filesystems.annotations.LayerGeneratingProcessor; import org.openide.filesystems.annotations.LayerGenerationException; import org.openide.util.lookup.ServiceProvider; @@ -94,7 +94,8 @@ HelpSetRegistration r = e.getAnnotation(HelpSetRegistration.class); String pkg = ((PackageElement) e).getQualifiedName().toString(); String hs = pkg.replace('.', '/') + '/' + r.helpSet(); - layer(e).file("Services/JavaHelp/" + pkg.replace('.', '-') + ".xml").contents("" + LayerBuilder builder = layer(e); + builder.file("Services/JavaHelp/" + pkg.replace('.', '-') + ".xml").contents("" + "\n" + "\n" + "\n" @@ -103,13 +104,7 @@ Document doc; URI loc; try { - FileObject hsf = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", hs); - try { // #181355 - hsf.openInputStream().close(); - } catch (FileNotFoundException x) { - hsf = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", hs); - } - loc = hsf.toUri(); + loc = builder.validateResource(hs, e, r, "helpSet", false).toUri(); if (loc.getScheme() == null) { // JDK #6419926: FileObject.toUri() generates URI without schema loc = new File(loc.toString()).toURI(); --- a/openide.awt/nbproject/project.xml +++ a/openide.awt/nbproject/project.xml @@ -54,7 +54,7 @@ - 7.50 + 7.51 --- a/openide.awt/src/org/netbeans/modules/openide/awt/ActionProcessor.java +++ a/openide.awt/src/org/netbeans/modules/openide/awt/ActionProcessor.java @@ -40,7 +40,6 @@ package org.netbeans.modules.openide.awt; import java.awt.event.ActionListener; -import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -64,12 +63,12 @@ import javax.swing.Action; import javax.swing.JSeparator; import javax.swing.KeyStroke; -import javax.tools.StandardLocation; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.awt.DynamicMenuContent; +import org.openide.filesystems.annotations.LayerBuilder; import org.openide.filesystems.annotations.LayerBuilder.File; import org.openide.filesystems.annotations.LayerGeneratingProcessor; import org.openide.filesystems.annotations.LayerGenerationException; @@ -186,7 +185,8 @@ throw new LayerGenerationException("@ActionID id() must be valid fully qualified name", e, processingEnv, aid, "id"); } String id = aid.id().replace('.', '-'); - File f = layer(e).file("Actions/" + aid.category() + "/" + id + ".instance"); + LayerBuilder builder = layer(e); + File f = builder.file("Actions/" + aid.category() + "/" + id + ".instance"); f.bundlevalue("displayName", ar.displayName(), ar, "displayName"); String menuText = ar.menuText(); @@ -228,7 +228,7 @@ key = ar.key(); } else { assert e.getKind() == ElementKind.METHOD : e; - layer(e).instanceFile("dummy", null, ActionListener.class, ar, null); + builder.instanceFile("dummy", null, ActionListener.class, ar, null); key = ar.key(); } @@ -263,23 +263,7 @@ } } if (ar.iconBase().length() > 0) { - boolean found = false; - for (StandardLocation l : StandardLocation.values()) { - try { - processingEnv.getFiler().getResource(l, "", ar.iconBase()); - found = true; - break; - } catch (IOException ex) { - continue; - } catch (IllegalArgumentException x) { - throw new LayerGenerationException("Problem with " + ar.iconBase() + " (should be resource path with no leading slash)", e, processingEnv, ar, "iconBase"); - } - } - if (!found) { - throw new LayerGenerationException( - "Cannot find iconBase file at " + ar.iconBase(), e, processingEnv, ar, "iconBase" - ); - } + builder.validateResource(ar.iconBase(), e, ar, "iconBase", true); f.stringvalue("iconBase", ar.iconBase()); } f.boolvalue("noIconInMenu", !ar.iconInMenu()); --- a/openide.filesystems/apichanges.xml +++ a/openide.filesystems/apichanges.xml @@ -49,6 +49,22 @@ Filesystems API + + + LayerBuilder can validate resources + + + + + +

+ LayerBuilder has a new validateResource method. + absolutizeResource was also added. +

+
+ + +
LayerGenerationException has new constructors --- a/openide.filesystems/manifest.mf +++ a/openide.filesystems/manifest.mf @@ -2,5 +2,5 @@ OpenIDE-Module: org.openide.filesystems OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml -OpenIDE-Module-Specification-Version: 7.50 +OpenIDE-Module-Specification-Version: 7.51 --- a/openide.filesystems/src/org/openide/filesystems/annotations/LayerBuilder.java +++ a/openide.filesystems/src/org/openide/filesystems/annotations/LayerBuilder.java @@ -45,8 +45,10 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectOutputStream; import java.lang.annotation.Annotation; import java.net.URI; +import java.net.URISyntaxException; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -66,6 +68,8 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.JavaFileManager.Location; import javax.tools.StandardLocation; import org.openide.util.NbBundle.Messages; import org.w3c.dom.Document; @@ -310,6 +314,93 @@ } /** + * Validates a resource named in an annotation. + *

Note that resources found in the binary classpath (if permitted) + * cannot actually be located when running inside javac on JDK 6 (see #196933 for discussion), in which case + * no exception is thrown but the return value may not permit {@link FileObject#openInputStream}. + *

Also remember that the binary compilation classpath for an Ant-based NetBeans module does + * not include non-public packages or indeed any non-classfiles from module dependencies. + * The processorpath does contain all of these but it is not consulted. + * The classpath for a Maven-based module does also contain resources from dependencies. + * @param resource an absolute resource path with no leading slash (perhaps the output of {@link #absolutizeResource}) + * @param originatingElement the annotated element; used both for error reporting, and (optionally) for its package + * @param annotation as in {@link LayerGenerationException#LayerGenerationException(String,Element,ProcessingEnvironment,Annotation,String)} + * @param annotationMethod as in {@link LayerGenerationException#LayerGenerationException(String,Element,ProcessingEnvironment,Annotation,String)} + * @param searchClasspath true to search in the binary classpath and not just source path (see caveat about JDK 6) + * @return the content of the resource, for further validation + * @throws LayerGenerationException if no such resource can be found + * @since 7.51 + */ + public FileObject validateResource(String resource, Element originatingElement, Annotation annotation, String annotationMethod, boolean searchClasspath) throws LayerGenerationException { + if (resource.startsWith("/")) { + throw new LayerGenerationException("do not use leading slashes on resource paths", originatingElement, processingEnv, annotation, annotationMethod); + } + if (searchClasspath) { + for (Location loc : new Location[] {StandardLocation.SOURCE_PATH, /* #181355 */StandardLocation.CLASS_OUTPUT, StandardLocation.CLASS_PATH, StandardLocation.PLATFORM_CLASS_PATH}) { + try { + return processingEnv.getFiler().getResource(loc, "", resource); + } catch (IOException ex) { + continue; + } + } + throw new LayerGenerationException("Cannot find resource " + resource, originatingElement, processingEnv, annotation, annotationMethod); + } else { + try { + try { + FileObject f = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", resource); + f.openInputStream().close(); + return f; + } catch (FileNotFoundException x) { + try { + FileObject f = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", resource); + f.openInputStream().close(); + return f; + } catch (IOException x2) { + throw x; + } + } + } catch (IOException x) { + throw new LayerGenerationException("Cannot find resource " + resource, originatingElement, processingEnv, annotation, annotationMethod); + } + } + } + + /** + * Allows a processor to accept relative resource paths. + * For example, to produce the output value {@code net/nowhere/lib/icon.png} + * given an element in the package {@code net.nowhere.app}, the following inputs are permitted: + *

    + *
  • {@code ../lib/icon.png} + *
  • {@code /net/nowhere/lib/icon.png} + *
+ * @param originatingElement the annotated element, used for its package + * @param resource a possibly relative resource path + * @return an absolute resource path (with no leading slash) + * @throws LayerGenerationException in case the resource path is malformed + * @since 7.51 + */ + public static String absolutizeResource(Element originatingElement, String resource) throws LayerGenerationException { + if (resource.startsWith("/")) { + return resource.substring(1); + } else { + try { + return new URI(null, findPackage(originatingElement).replace('.', '/') + "/", null).resolve(new URI(null, resource, null)).getPath(); + } catch (URISyntaxException x) { + throw new LayerGenerationException(x.toString(), originatingElement); + } + } + } + private static String findPackage(Element e) { + switch (e.getKind()) { + case PACKAGE: + return ((PackageElement) e).getQualifiedName().toString(); + default: + return findPackage(e.getEnclosingElement()); + } + } + + + /** * Builder for creating a single file entry. */ public final class File { @@ -627,29 +718,19 @@ } } } - String resource = bundle.replace('.', '/') + ".properties"; try { - InputStream is; - try { - is = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", resource).openInputStream(); - } catch (FileNotFoundException x) { // #181355 - try { - is = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", resource).openInputStream(); - } catch (IOException x2) { - throw x; - } - } + InputStream is = validateResource(bundle.replace('.', '/') + ".properties", originatingElement, null, null, false).openInputStream(); try { Properties p = new Properties(); p.load(is); if (p.getProperty(key) == null) { - throw new LayerGenerationException("No key '" + key + "' found in " + resource, originatingElement, processingEnv, annotation, annotationMethod); + throw new LayerGenerationException("No key '" + key + "' found in " + bundle, originatingElement, processingEnv, annotation, annotationMethod); } } finally { is.close(); } } catch (IOException x) { - throw new LayerGenerationException("Could not open " + resource + ": " + x, originatingElement, processingEnv, annotation, annotationMethod); + throw new LayerGenerationException("Could not open " + bundle + ": " + x, originatingElement, processingEnv, annotation, annotationMethod); } } --- a/openide.filesystems/test/unit/src/org/openide/filesystems/annotations/LayerBuilderTest.java +++ a/openide.filesystems/test/unit/src/org/openide/filesystems/annotations/LayerBuilderTest.java @@ -46,6 +46,8 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; +import java.net.URL; +import java.net.URLClassLoader; import java.util.Collections; import java.util.List; import java.util.Set; @@ -61,6 +63,7 @@ import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; +import javax.tools.ToolProvider; import org.netbeans.junit.NbTestCase; import org.openide.util.lookup.ServiceProvider; import org.openide.util.test.AnnotationProcessorTestUtils; @@ -295,4 +298,20 @@ } } + public void testValidateResourceExistent() throws Exception { + // XXX + } + + public void testValidateResourceNonexistent() throws Exception { + if (new URLClassLoader(new URL[] {ToolProvider.getSystemJavaCompiler().getClass().getProtectionDomain().getCodeSource().getLocation()}).findResource("com/sun/tools/javac/util/Filter.class") == null) { + System.err.println("#196933: testValidateResourceNonexistent will only pass when using JDK 7 javac, skipping"); + return; + } + // XXX + } + + public void testAbsolutizeResource() throws Exception { + // XXX + } + } --- a/options.api/nbproject/project.xml +++ a/options.api/nbproject/project.xml @@ -120,7 +120,7 @@ - 7.50 + 7.51
--- a/options.api/src/org/netbeans/modules/options/OptionsPanelControllerProcessor.java +++ a/options.api/src/org/netbeans/modules/options/OptionsPanelControllerProcessor.java @@ -42,8 +42,6 @@ package org.netbeans.modules.options; -import java.io.FileNotFoundException; -import java.io.IOException; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.HashSet; @@ -54,13 +52,13 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; -import javax.tools.StandardLocation; import org.netbeans.spi.options.AdvancedOption; import org.netbeans.spi.options.OptionsCategory; import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController.ContainerRegistration; import org.netbeans.spi.options.OptionsPanelController.SubRegistration; import org.netbeans.spi.options.OptionsPanelController.TopLevelRegistration; +import org.openide.filesystems.annotations.LayerBuilder; import org.openide.filesystems.annotations.LayerBuilder.File; import org.openide.filesystems.annotations.LayerGeneratingProcessor; import org.openide.filesystems.annotations.LayerGenerationException; @@ -84,12 +82,13 @@ } for (Element e : roundEnv.getElementsAnnotatedWith(TopLevelRegistration.class)) { TopLevelRegistration r = e.getAnnotation(TopLevelRegistration.class); - File file = layer(e).instanceFile("OptionsDialog", r.id().length() > 0 ? r.id() : null, r, null). + LayerBuilder builder = layer(e); + File file = builder.instanceFile("OptionsDialog", r.id().length() > 0 ? r.id() : null, r, null). methodvalue("instanceCreate", OptionsCategory.class.getName(), "createCategory"). instanceAttribute("controller", OptionsPanelController.class). bundlevalue("categoryName", r.categoryName()). position(r.position()); - iconBase(e, r.iconBase(), r, file); + iconBase(e, r.iconBase(), r, file, builder); keywords(e, r.keywords(), r.keywordsCategory(), r, file); file.write(); } @@ -108,12 +107,13 @@ } for (Element e : roundEnv.getElementsAnnotatedWith(ContainerRegistration.class)) { ContainerRegistration r = e.getAnnotation(ContainerRegistration.class); - File file = layer(e).file("OptionsDialog/" + r.id() + ".instance"). + LayerBuilder builder = layer(e); + File file = builder.file("OptionsDialog/" + r.id() + ".instance"). methodvalue("instanceCreate", OptionsCategory.class.getName(), "createCategory"). stringvalue("advancedOptionsFolder", "OptionsDialog/" + r.id()). bundlevalue("categoryName", r.categoryName()). position(r.position()); - iconBase(e, r.iconBase(), r, file); + iconBase(e, r.iconBase(), r, file, builder); keywords(e, r.keywords(), r.keywordsCategory(), r, file); file.write(); layer(e).folder("OptionsDialog/" + r.id()).position(0).write(); @@ -121,20 +121,8 @@ return true; } - private void iconBase(Element e, String iconBase, Annotation r, File file) throws LayerGenerationException { - try { // XXX should probably be made a utility method in LayerBuilder - try { - processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", iconBase).openInputStream().close(); - } catch (FileNotFoundException x) { - try { - processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", iconBase).openInputStream().close(); - } catch (IOException x2) { - throw x; - } - } - } catch (IOException x) { - throw new LayerGenerationException("Could not open " + iconBase + ": " + x, e, processingEnv, r, "iconBase"); - } + private void iconBase(Element e, String iconBase, Annotation r, File file, LayerBuilder builder) throws LayerGenerationException { + builder.validateResource(iconBase, e, r, "iconBase", false); file.stringvalue("iconBase", iconBase); } --- a/schema2beans/src/org/netbeans/modules/schema2beansdev/Schema2BeansProcessor.java +++ a/schema2beans/src/org/netbeans/modules/schema2beansdev/Schema2BeansProcessor.java @@ -189,6 +189,7 @@ } private FileObject findResource(String path, String pkg) throws URISyntaxException, IOException { + // XXX LayerBuilder has standard versions of this logic now String abspath; if (path.startsWith("/")) { abspath = path.substring(1);