This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 149136
Collapse All | Expand All

(-)a/apisupport.project/src/org/netbeans/modules/apisupport/project/layers/LayerUtils.java (+20 lines)
Lines 620-625 Link Here
620
                    if (roLayer != null) {
620
                    if (roLayer != null) {
621
                        readOnlyLayers.add(roLayer);
621
                        readOnlyLayers.add(roLayer);
622
                    }
622
                    }
623
                    // XXX could also look for ${sister}/build/classes/META-INF/generated-layer.xml
623
                }
624
                }
624
                Set<File> jars = getPlatformJarsForSuiteComponentProject(p, suite);
625
                Set<File> jars = getPlatformJarsForSuiteComponentProject(p, suite);
625
                readOnlyLayers.addAll(Arrays.asList(getPlatformLayers(jars)));
626
                readOnlyLayers.addAll(Arrays.asList(getPlatformLayers(jars)));
Lines 650-655 Link Here
650
                        continue;
651
                        continue;
651
                    }
652
                    }
652
                    otherLayerURLs.add(layerXml.getURL());
653
                    otherLayerURLs.add(layerXml.getURL());
654
                    // XXX as above, could add generated-layer.xml
653
                }
655
                }
654
                XMLFileSystem xfs = new XMLFileSystem();
656
                XMLFileSystem xfs = new XMLFileSystem();
655
                try {
657
                try {
Lines 780-785 Link Here
780
                    throw (IOException) new IOException(e.toString()).initCause(e);
782
                    throw (IOException) new IOException(e.toString()).initCause(e);
781
                }
783
                }
782
            }
784
            }
785
            { // #149136: load generated layers too
786
                // XXX might be faster to use ManifestManager's original opening of JAR to see if it really had such a layer
787
                URL generatedLayer = new URL("jar:" + jar.toURI() + "!/META-INF/generated-layer.xml");
788
                boolean ok = true;
789
                try {
790
                    generatedLayer.openConnection().connect();
791
                } catch (IOException x) {
792
                    // ignore, probably just means resource does not exist
793
                    ok = false;
794
                }
795
                if (ok) {
796
                    try {
797
                        layers.add(new XMLFileSystem(generatedLayer));
798
                    } catch (SAXException x) {
799
                        throw (IOException) new IOException(x.toString()).initCause(x);
800
                    }
801
                }
802
            }
783
        }
803
        }
784
        return layers.toArray(new FileSystem[layers.size()]);
804
        return layers.toArray(new FileSystem[layers.size()]);
785
    }
805
    }
(-)a/core.startup/src/org/netbeans/core/startup/NbInstaller.java (-20 / +31 lines)
Lines 45-55 Link Here
45
import java.io.DataOutputStream;
45
import java.io.DataOutputStream;
46
import java.io.File;
46
import java.io.File;
47
import java.io.IOException;
47
import java.io.IOException;
48
import java.lang.reflect.Method;
48
import java.net.URL;
49
import java.net.URL;
49
import java.nio.ByteBuffer;
50
import java.nio.ByteBuffer;
50
import java.util.ArrayList;
51
import java.util.ArrayList;
51
import java.util.Collections;
52
import java.util.Collections;
52
import java.util.Date;
53
import java.util.Date;
54
import java.util.Enumeration;
53
import java.util.HashMap;
55
import java.util.HashMap;
54
import java.util.HashSet;
56
import java.util.HashSet;
55
import java.util.LinkedList;
57
import java.util.LinkedList;
Lines 542-550 Link Here
542
        modules = new ArrayList<Module>(modules);
544
        modules = new ArrayList<Module>(modules);
543
        Collections.reverse(modules);
545
        Collections.reverse(modules);
544
        Map<ModuleLayeredFileSystem,List<URL>> urls = new HashMap<ModuleLayeredFileSystem,List<URL>>(5);
546
        Map<ModuleLayeredFileSystem,List<URL>> urls = new HashMap<ModuleLayeredFileSystem,List<URL>>(5);
545
        urls.put(ModuleLayeredFileSystem.getUserModuleLayer(), new ArrayList<URL>(1000));
547
        ModuleLayeredFileSystem userModuleLayer = ModuleLayeredFileSystem.getUserModuleLayer();
546
        urls.put(ModuleLayeredFileSystem.getInstallationModuleLayer(), new ArrayList<URL>(1000));
548
        ModuleLayeredFileSystem installationModuleLayer = ModuleLayeredFileSystem.getInstallationModuleLayer();
549
        urls.put(userModuleLayer, new ArrayList<URL>(1000));
550
        urls.put(installationModuleLayer, new ArrayList<URL>(1000));
547
        for (Module m: modules) {
551
        for (Module m: modules) {
552
            // #19458: only put reloadables into the "session layer"
553
            // (where they will not have their layers cached). All others
554
            // should go into "installation layer" (so that they can mask
555
            // layers according to cross-dependencies).
556
            ModuleLayeredFileSystem host = m.isReloadable() ? userModuleLayer : installationModuleLayer;
557
            List<URL> theseurls = urls.get(host);
558
            if (theseurls == null) {
559
                theseurls = new ArrayList<URL>(1000);
560
                urls.put(host, theseurls);
561
            }
562
            ClassLoader cl = m.getClassLoader();
548
            String s = layers.get(m);
563
            String s = layers.get(m);
549
            if (s != null) {
564
            if (s != null) {
550
                Util.err.fine("loadLayer: " + s + " load=" + load);
565
                Util.err.fine("loadLayer: " + s + " load=" + load);
Lines 557-578 Link Here
557
                } else {
572
                } else {
558
                    base = s.substring(0, idx);
573
                    base = s.substring(0, idx);
559
                    ext = s.substring(idx);
574
                    ext = s.substring(idx);
560
                }
561
                ClassLoader cl = m.getClassLoader();
562
                ModuleLayeredFileSystem host;
563
                // #19458: only put reloadables into the "session layer"
564
                // (where they will not have their layers cached). All others
565
                // should go into "installation layer" (so that they can mask
566
                // layers according to cross-dependencies).
567
                if (m.isReloadable()) {
568
                    host = ModuleLayeredFileSystem.getUserModuleLayer();
569
                } else {
570
                    host = ModuleLayeredFileSystem.getInstallationModuleLayer();
571
                }
572
                List<URL> theseurls = urls.get(host);
573
                if (theseurls == null) {
574
                    theseurls = new ArrayList<URL>(1000);
575
                    urls.put(host, theseurls);
576
                }
575
                }
577
                boolean foundSomething = false;
576
                boolean foundSomething = false;
578
                for (String suffix : NbCollections.iterable(NbBundle.getLocalizingSuffixes())) {
577
                for (String suffix : NbCollections.iterable(NbBundle.getLocalizingSuffixes())) {
Lines 589-594 Link Here
589
                    continue;
588
                    continue;
590
                }
589
                }
591
            }
590
            }
591
            try { // #149136
592
                // Cannot use getResources because we do not wish to delegate to parents.
593
                // In fact both URLClassLoader and ProxyClassLoader override this method to be public.
594
                Method findResources = ClassLoader.class.getDeclaredMethod("findResources", String.class); // NOI18N
595
                findResources.setAccessible(true);
596
                Enumeration e = (Enumeration) findResources.invoke(cl, "META-INF/generated-layer.xml"); // NOI18N
597
                while (e.hasMoreElements()) {
598
                    theseurls.add((URL) e.nextElement());
599
                }
600
            } catch (Exception x) {
601
                Exceptions.printStackTrace(x);
602
            }
592
        }
603
        }
593
        // Now actually do it.
604
        // Now actually do it.
594
        for (Map.Entry<ModuleLayeredFileSystem,List<URL>> entry: urls.entrySet()) {
605
        for (Map.Entry<ModuleLayeredFileSystem,List<URL>> entry: urls.entrySet()) {
Lines 601-608 Link Here
601
                } else {
612
                } else {
602
                    // #106737: we might have the wrong host, since it switches when reloadable flag is toggled.
613
                    // #106737: we might have the wrong host, since it switches when reloadable flag is toggled.
603
                    // To be safe, remove from both.
614
                    // To be safe, remove from both.
604
                    ModuleLayeredFileSystem.getUserModuleLayer().removeURLs(theseurls);
615
                    userModuleLayer.removeURLs(theseurls);
605
                    ModuleLayeredFileSystem.getInstallationModuleLayer().removeURLs(theseurls);
616
                    installationModuleLayer.removeURLs(theseurls);
606
                }
617
                }
607
            } catch (Exception e) {
618
            } catch (Exception e) {
608
                Util.err.log(Level.WARNING, null, e);
619
                Util.err.log(Level.WARNING, null, e);
(-)a/core.startup/src/org/netbeans/core/startup/layers/ModuleLayeredFileSystem.java (+3 lines)
Lines 193-198 Link Here
193
                        is.close();
193
                        is.close();
194
                    }
194
                    }
195
                }
195
                }
196
                for (URL generatedLayer : NbCollections.iterable(loader.getResources("META-INF/generated-layer.xml"))) { // NOI18N
197
                    layerUrls.add(generatedLayer);
198
                }
196
                XMLFileSystem xmlfs = new XMLFileSystem();
199
                XMLFileSystem xmlfs = new XMLFileSystem();
197
                xmlfs.setXmlUrls(layerUrls.toArray(new URL[layerUrls.size()]));
200
                xmlfs.setXmlUrls(layerUrls.toArray(new URL[layerUrls.size()]));
198
                l.add(xmlfs);
201
                l.add(xmlfs);
(-)a/o.n.bootstrap/src/org/netbeans/JarClassLoader.java (-2 / +2 lines)
Lines 222-228 Link Here
222
    }
222
    }
223
    // look up the jars and return a resource based on a content of jars
223
    // look up the jars and return a resource based on a content of jars
224
    @Override
224
    @Override
225
    protected URL findResource(String name) {
225
    public URL findResource(String name) {
226
        for( int i=0; i<sources.length; i++ ) {
226
        for( int i=0; i<sources.length; i++ ) {
227
            URL item = sources[i].getResource(name);
227
            URL item = sources[i].getResource(name);
228
            if (item != null) return item;
228
            if (item != null) return item;
Lines 231-237 Link Here
231
    }
231
    }
232
232
233
    @Override
233
    @Override
234
    protected Enumeration<URL> simpleFindResources(String name) {
234
    public Enumeration<URL> findResources(String name) {
235
        Vector<URL> v = new Vector<URL>(3);
235
        Vector<URL> v = new Vector<URL>(3);
236
        // look up the jars and return a resource based on a content of jars
236
        // look up the jars and return a resource based on a content of jars
237
237
(-)a/o.n.bootstrap/src/org/netbeans/MainImpl.java (-1 / +1 lines)
Lines 250-256 Link Here
250
                if (cp.isEmpty ()) {
250
                if (cp.isEmpty ()) {
251
                    value = searchBuildNumber(this.getResources("META-INF/MANIFEST.MF"));
251
                    value = searchBuildNumber(this.getResources("META-INF/MANIFEST.MF"));
252
                } else {
252
                } else {
253
                    value = searchBuildNumber(this.simpleFindResources("META-INF/MANIFEST.MF"));
253
                    value = searchBuildNumber(this.findResources("META-INF/MANIFEST.MF"));
254
                }
254
                }
255
            } catch (IOException ex) {
255
            } catch (IOException ex) {
256
                ex.printStackTrace();
256
                ex.printStackTrace();
(-)a/o.n.bootstrap/src/org/netbeans/ProxyClassLoader.java (-8 / +13 lines)
Lines 362-369 Link Here
362
     *      if the resource could not be found.
362
     *      if the resource could not be found.
363
     */
363
     */
364
    @Override
364
    @Override
365
    protected URL findResource(String name) {
365
    public URL findResource(String name) {
366
	return null;
366
        return super.findResource(name);
367
    }
367
    }
368
    
368
    
369
    /**
369
    /**
Lines 377-383 Link Here
377
     * @throws IOException if I/O errors occur
377
     * @throws IOException if I/O errors occur
378
     */    
378
     */    
379
    @Override
379
    @Override
380
    protected final synchronized Enumeration<URL> findResources(String name) throws IOException {
380
    public final synchronized Enumeration<URL> getResources(String name) throws IOException {
381
        name = stripInitialSlash(name);
381
        name = stripInitialSlash(name);
382
        final int slashIdx = name.lastIndexOf('/');
382
        final int slashIdx = name.lastIndexOf('/');
383
        if (slashIdx == -1) {
383
        if (slashIdx == -1) {
Lines 398-413 Link Here
398
            if (del != null) { // unclaimed resource, go directly to SCL
398
            if (del != null) { // unclaimed resource, go directly to SCL
399
                for (ProxyClassLoader pcl : parents) { // all our accessible parents
399
                for (ProxyClassLoader pcl : parents) { // all our accessible parents
400
                    if (del.contains(pcl) && shouldDelegateResource(path, pcl)) { // that cover given package
400
                    if (del.contains(pcl) && shouldDelegateResource(path, pcl)) { // that cover given package
401
                        sub.add(pcl.simpleFindResources(name));
401
                        sub.add(pcl.findResources(name));
402
                    }
402
                    }
403
                }
403
                }
404
                if (del.contains(this)) sub.add(simpleFindResources(name)); 
404
                if (del.contains(this)) {
405
                    sub.add(findResources(name));
406
                }
405
            }
407
            }
406
        } else { // Don't bother optimizing this call by domains.
408
        } else { // Don't bother optimizing this call by domains.
407
            for (ProxyClassLoader pcl : parents) { 
409
            for (ProxyClassLoader pcl : parents) { 
408
                if (shouldDelegateResource(path, pcl)) sub.add(pcl.simpleFindResources(name)); 
410
                if (shouldDelegateResource(path, pcl)) {
411
                    sub.add(pcl.findResources(name));
412
                }
409
            }
413
            }
410
            sub.add(simpleFindResources(name));
414
            sub.add(findResources(name));
411
        }
415
        }
412
        // Should not be duplicates, assuming the parent loaders are properly distinct
416
        // Should not be duplicates, assuming the parent loaders are properly distinct
413
        // from one another and do not overlap in JAR usage, which they ought not.
417
        // from one another and do not overlap in JAR usage, which they ought not.
Lines 416-422 Link Here
416
        return Enumerations.concat(Collections.enumeration(sub));
420
        return Enumerations.concat(Collections.enumeration(sub));
417
    }
421
    }
418
422
419
    protected Enumeration<URL> simpleFindResources(String name) throws IOException {
423
    @Override
424
    public Enumeration<URL> findResources(String name) throws IOException {
420
        return super.findResources(name);
425
        return super.findResources(name);
421
    }
426
    }
422
427
(-)a/o.n.bootstrap/test/unit/src/org/netbeans/ProxyClassLoaderTest.java (-36 / +97 lines)
Lines 42-49 Link Here
42
import java.io.ByteArrayOutputStream;
42
import java.io.ByteArrayOutputStream;
43
import java.io.IOException;
43
import java.io.IOException;
44
import java.io.InputStream;
44
import java.io.InputStream;
45
import java.net.MalformedURLException;
46
import java.net.URL;
47
import java.util.Arrays;
45
import java.util.Collections;
48
import java.util.Collections;
49
import java.util.Enumeration;
46
import junit.framework.TestCase;
50
import junit.framework.TestCase;
51
import org.openide.util.Enumerations;
52
import org.openide.util.Exceptions;
47
53
48
public class ProxyClassLoaderTest extends TestCase {
54
public class ProxyClassLoaderTest extends TestCase {
49
55
Lines 52-57 Link Here
52
    }
58
    }
53
59
54
    public void testAmbiguousDelegation() throws Exception {
60
    public void testAmbiguousDelegation() throws Exception {
61
        class CL extends ProxyClassLoader {
62
            final Class[] owned;
63
            final String name;
64
            CL(ClassLoader[] parents, String name, Class... owned) {
65
                super(parents, false);
66
                addCoveredPackages(Collections.singleton("org.netbeans"));
67
                this.name = name;
68
                this.owned = owned;
69
            }
70
            protected @Override Class doLoadClass(String pkg, String name) {
71
                for (Class c : owned) {
72
                    if (name.equals(c.getName())) {
73
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
74
                        InputStream is = CL.class.getClassLoader().getResourceAsStream(name.replace('.', '/') + ".class");
75
                        byte[] buf = new byte[4096];
76
                        int read;
77
                        try {
78
                            while ((read = is.read(buf)) != -1) {
79
                                baos.write(buf, 0, read);
80
                            }
81
                        } catch (IOException x) {
82
                            assert false : x;
83
                        }
84
                        return defineClass(name, baos.toByteArray(), 0, baos.size());
85
                    }
86
                }
87
                return null;
88
            }
89
            protected @Override boolean shouldDelegateResource(String pkg, ClassLoader parent) {
90
                return parent != null || !pkg.equals("org/netbeans/");
91
            }
92
            public @Override String toString() {
93
                return name;
94
            }
95
        }
55
        ClassLoader l1 = new CL(new ClassLoader[0], "l1", A.class);
96
        ClassLoader l1 = new CL(new ClassLoader[0], "l1", A.class);
56
        ClassLoader l2 = new CL(new ClassLoader[0], "l2", A.class);
97
        ClassLoader l2 = new CL(new ClassLoader[0], "l2", A.class);
57
        ClassLoader l3 = new CL(new ClassLoader[] {l1}, "l3", B.class);
98
        ClassLoader l3 = new CL(new ClassLoader[] {l1}, "l3", B.class);
Lines 76-117 Link Here
76
        assertEquals(l1, l5.loadClass(C.class.getName()).getMethod("a").invoke(null).getClass().getClassLoader());
117
        assertEquals(l1, l5.loadClass(C.class.getName()).getMethod("a").invoke(null).getClass().getClassLoader());
77
    }
118
    }
78
119
79
    static class CL extends ProxyClassLoader {
80
        final Class[] owned;
81
        final String name;
82
        CL(ClassLoader[] parents, String name, Class... owned) {
83
            super(parents, false);
84
            addCoveredPackages(Collections.singleton("org.netbeans"));
85
            this.name = name;
86
            this.owned = owned;
87
        }
88
        protected @Override Class doLoadClass(String pkg, String name) {
89
            for (Class c : owned) {
90
                if (name.equals(c.getName())) {
91
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
92
                    InputStream is = CL.class.getClassLoader().getResourceAsStream(name.replace('.', '/') + ".class");
93
                    byte[] buf = new byte[4096];
94
                    int read;
95
                    try {
96
                        while ((read = is.read(buf)) != -1) {
97
                            baos.write(buf, 0, read);
98
                        }
99
                    } catch (IOException x) {
100
                        assert false : x;
101
                    }
102
                    return defineClass(name, baos.toByteArray(), 0, baos.size());
103
                }
104
            }
105
            return null;
106
        }
107
        protected @Override boolean shouldDelegateResource(String pkg, ClassLoader parent) {
108
            return parent != null || !pkg.equals("org/netbeans/");
109
        }
110
        public @Override String toString() {
111
            return name;
112
        }
113
    }
114
115
    public static class A {}
120
    public static class A {}
116
    public static class B {
121
    public static class B {
117
        public static A a() {
122
        public static A a() {
Lines 124-127 Link Here
124
        }
129
        }
125
    }
130
    }
126
131
132
    public void testResourceDelegation() throws Exception { // #32576
133
        class CL extends ProxyClassLoader {
134
            final URL base1, base2;
135
            final String[] owned;
136
            CL(ClassLoader[] parents, URL base1, URL base2, String... owned) {
137
                super(parents, false);
138
                this.base1 = base1;
139
                this.base2 = base2;
140
                this.owned = owned;
141
                addCoveredPackages(Collections.singleton("p"));
142
            }
143
            @Override public URL findResource(String name) {
144
                if (Arrays.asList(owned).contains(name)) {
145
                    try {
146
                        return new URL(base1, name);
147
                    } catch (MalformedURLException ex) {
148
                        Exceptions.printStackTrace(ex);
149
                    }
150
                }
151
                return null;
152
            }
153
            @Override public synchronized Enumeration<URL> findResources(String name) throws IOException {
154
                if (Arrays.asList(owned).contains(name)) {
155
                    return Enumerations.array(new URL(base1, name), new URL(base2, name));
156
                }
157
                return super.findResources(name);
158
            }
159
        }
160
        URL b = new URL("http://nowhere.net/");
161
        ProxyClassLoader cl1 = new CL(new ClassLoader[0], new URL(b, "1a/"), new URL(b, "1b/"), "p/1");
162
        ProxyClassLoader cl2 = new CL(new ClassLoader[] {cl1}, new URL(b, "2a/"), new URL(b, "2b/"), "p/2");
163
        ProxyClassLoader cl3 = new CL(new ClassLoader[] {cl1}, new URL(b, "3a/"), new URL(b, "3b/"), "p/1", "p/3");
164
        ProxyClassLoader cl4 = new CL(new ClassLoader[] {cl1, cl2, cl3}, new URL(b, "4a/"), new URL(b, "4b/"));
165
        assertEquals(new URL(b, "1a/p/1"), cl1.getResource("p/1"));
166
        assertEquals(null, cl1.getResource("p/1x"));
167
        assertEquals(Arrays.asList(new URL(b, "1a/p/1"), new URL(b, "1b/p/1")), Collections.list(cl1.getResources("p/1")));
168
        assertEquals(new URL(b, "1a/p/1"), cl2.getResource("p/1"));
169
        assertEquals(null, cl2.findResource("p/1"));
170
        assertEquals(new URL(b, "2a/p/2"), cl2.getResource("p/2"));
171
        assertEquals(new URL(b, "2a/p/2"), cl2.findResource("p/2"));
172
        assertEquals(Arrays.asList(new URL(b, "2a/p/2"), new URL(b, "2b/p/2")), Collections.list(cl2.getResources("p/2")));
173
        assertEquals(null, cl2.findResource("p/1"));
174
        assertEquals(new URL(b, "1a/p/1"), cl3.getResource("p/1"));
175
        assertEquals(new URL(b, "3a/p/1"), cl3.findResource("p/1"));
176
        assertEquals(Arrays.asList(new URL(b, "1a/p/1"), new URL(b, "1b/p/1"), new URL(b, "3a/p/1"), new URL(b, "3b/p/1")),
177
                Collections.list(cl3.getResources("p/1")));
178
        assertEquals(Arrays.asList(new URL(b, "3a/p/1"), new URL(b, "3b/p/1")), Collections.list(cl3.findResources("p/1")));
179
        assertEquals(new URL(b, "1a/p/1"), cl4.getResource("p/1"));
180
        assertEquals(new URL(b, "2a/p/2"), cl4.getResource("p/2"));
181
        assertEquals(new URL(b, "3a/p/3"), cl4.getResource("p/3"));
182
        assertEquals(Arrays.asList(new URL(b, "1a/p/1"), new URL(b, "1b/p/1"), new URL(b, "3a/p/1"), new URL(b, "3b/p/1")),
183
                Collections.list(cl4.getResources("p/1")));
184
        assertEquals(Arrays.asList(new URL(b, "2a/p/2"), new URL(b, "2b/p/2")), Collections.list(cl4.getResources("p/2")));
185
        assertEquals(Arrays.asList(new URL(b, "3a/p/3"), new URL(b, "3b/p/3")), Collections.list(cl4.getResources("p/3")));
186
    }
187
127
}
188
}
(-)a/openide.filesystems/nbproject/project.properties (+1 lines)
Lines 40-45 Link Here
40
javac.compilerargs=-Xlint -Xlint:-serial
40
javac.compilerargs=-Xlint -Xlint:-serial
41
javac.source=1.5
41
javac.source=1.5
42
module.jar.dir=core
42
module.jar.dir=core
43
cp.extra=${nb_all}/libs.javacapi/external/javac-api-nb-7.0-b07.jar
43
javadoc.main.page=org/openide/filesystems/doc-files/api.html
44
javadoc.main.page=org/openide/filesystems/doc-files/api.html
44
javadoc.arch=${basedir}/arch.xml
45
javadoc.arch=${basedir}/arch.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
46
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/openide.filesystems/nbproject/project.xml (+1 lines)
Lines 75-80 Link Here
75
            </test-dependencies>
75
            </test-dependencies>
76
            <public-packages>
76
            <public-packages>
77
                <package>org.openide.filesystems</package>
77
                <package>org.openide.filesystems</package>
78
                <package>org.openide.filesystems.annotations</package>
78
            </public-packages>
79
            </public-packages>
79
        </data>
80
        </data>
80
    </configuration>
81
    </configuration>
(-)a/openide.filesystems/src/org/openide/filesystems/ExternalUtil.java (+3 lines)
Lines 213-218 Link Here
213
                        is.close();
213
                        is.close();
214
                    }
214
                    }
215
                }
215
                }
216
                for (URL generatedLayer : NbCollections.iterable(l.getResources("META-INF/generated-layer.xml"))) { // NOI18N
217
                    layerUrls.add(generatedLayer);
218
                }
216
                layers.setXmlUrls(layerUrls.toArray(new URL[layerUrls.size()]));
219
                layers.setXmlUrls(layerUrls.toArray(new URL[layerUrls.size()]));
217
                LOG.log(Level.FINE, "Loading classpath layers: {0}", layerUrls);
220
                LOG.log(Level.FINE, "Loading classpath layers: {0}", layerUrls);
218
            } catch (Exception x) {
221
            } catch (Exception x) {
(-)a/openide.filesystems/src/org/openide/filesystems/annotations/LayerBuilder.java (+342 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2008 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.filesystems.annotations;
41
42
import java.net.URL;
43
import java.util.Arrays;
44
import java.util.LinkedHashMap;
45
import java.util.Map;
46
import org.w3c.dom.Document;
47
import org.w3c.dom.Element;
48
import org.w3c.dom.NodeList;
49
50
/**
51
 * Convenience class for generating fragments of an XML layer.
52
 * @see LayerGeneratingProcessor#layer
53
 */
54
public final class LayerBuilder {
55
56
    private final Document doc;
57
58
    /**
59
     * Creates a new builder.
60
     * @param document a DOM representation of an XML layer which will be modified
61
     */
62
    public LayerBuilder(Document document) {
63
        this.doc = document;
64
    }
65
66
    /**
67
     * Adds a file to the layer.
68
     * You need to {@link File#write} it in order to finalize the effect.
69
     * @param path the full path to the desired file in resource format, e.g. {@code "Menu/File/exit.instance"}
70
     * @return a file builder
71
     */
72
    public File file(String path) {
73
        return new File(path);
74
    }
75
76
    /**
77
     * Builder for creating a single file entry.
78
     */
79
    public final class File {
80
81
        private final String path;
82
        private final Map<String,String[]> attrs = new LinkedHashMap<String,String[]>();
83
        private String contents;
84
        private String url;
85
86
        File(String path) {
87
            this.path = path;
88
        }
89
90
        /**
91
         * Gets the path this file is to be created under.
92
         * @return the configured path, as in {@link #file}
93
         */
94
        public String getPath() {
95
            return path;
96
        }
97
98
        /**
99
         * Configures the file to have inline text contents.
100
         * @param contents text to use as the body of the file
101
         * @return this builder
102
         */
103
        public File contents(String contents) {
104
            if (this.contents != null || url != null || contents == null) {
105
                throw new IllegalArgumentException();
106
            }
107
            this.contents = contents;
108
            return this;
109
        }
110
111
        /**
112
         * Configures the file to have external contents.
113
         * @param url a URL to the body of the file, e.g. {@code "nbresloc:/org/my/module/resources/definition.xml"}
114
         *            or more commonly an absolute resource path such as {@code "/org/my/module/resources/definition.xml"}
115
         * @return this builder
116
         */
117
        public File url(String url) {
118
            if (contents != null || this.url != null || url == null) {
119
                throw new IllegalArgumentException();
120
            }
121
            this.url = url;
122
            return this;
123
        }
124
125
        /**
126
         * Adds a string-valued attribute.
127
         * @param attr the attribute name
128
         * @param value the attribute value
129
         * @return this builder
130
         */
131
        public File stringvalue(String attr, String value) {
132
            attrs.put(attr, new String[] {"stringvalue", value});
133
            return this;
134
        }
135
136
        /**
137
         * Adds a byte-valued attribute.
138
         * @param attr the attribute name
139
         * @param value the attribute value
140
         * @return this builder
141
         */
142
        public File bytevalue(String attr, byte value) {
143
            attrs.put(attr, new String[] {"bytevalue", Byte.toString(value)});
144
            return this;
145
        }
146
147
        /**
148
         * Adds a short-valued attribute.
149
         * @param attr the attribute name
150
         * @param value the attribute value
151
         * @return this builder
152
         */
153
        public File shortvalue(String attr, short value) {
154
            attrs.put(attr, new String[] {"shortvalue", Short.toString(value)});
155
            return this;
156
        }
157
158
        /**
159
         * Adds an int-valued attribute.
160
         * @param attr the attribute name
161
         * @param value the attribute value
162
         * @return this builder
163
         */
164
        public File intvalue(String attr, int value) {
165
            attrs.put(attr, new String[] {"intvalue", Integer.toString(value)});
166
            return this;
167
        }
168
169
        /**
170
         * Adds a long-valued attribute.
171
         * @param attr the attribute name
172
         * @param value the attribute value
173
         * @return this builder
174
         */
175
        public File longvalue(String attr, long value) {
176
            attrs.put(attr, new String[] {"longvalue", Long.toString(value)});
177
            return this;
178
        }
179
180
        /**
181
         * Adds a float-valued attribute.
182
         * @param attr the attribute name
183
         * @param value the attribute value
184
         * @return this builder
185
         */
186
        public File floatvalue(String attr, float value) {
187
            attrs.put(attr, new String[] {"floatvalue", Float.toString(value)});
188
            return this;
189
        }
190
191
        /**
192
         * Adds a double-valued attribute.
193
         * @param attr the attribute name
194
         * @param value the attribute value
195
         * @return this builder
196
         */
197
        public File doublevalue(String attr, double value) {
198
            attrs.put(attr, new String[] {"doublevalue", Double.toString(value)});
199
            return this;
200
        }
201
202
        /**
203
         * Adds a boolean-valued attribute.
204
         * @param attr the attribute name
205
         * @param value the attribute value
206
         * @return this builder
207
         */
208
        public File boolvalue(String attr, boolean value) {
209
            attrs.put(attr, new String[] {"boolvalue", Boolean.toString(value)});
210
            return this;
211
        }
212
213
        /**
214
         * Adds a character-valued attribute.
215
         * @param attr the attribute name
216
         * @param value the attribute value
217
         * @return this builder
218
         */
219
        public File charvalue(String attr, char value) {
220
            attrs.put(attr, new String[] {"charvalue", Character.toString(value)});
221
            return this;
222
        }
223
224
        /**
225
         * Adds a URL-valued attribute.
226
         * @param attr the attribute name
227
         * @param value the attribute value
228
         * @return this builder
229
         */
230
        public File urlvalue(String attr, URL value) {
231
            attrs.put(attr, new String[] {"urlvalue", value.toString()});
232
            return this;
233
        }
234
235
        /**
236
         * Adds an attribute loaded from a Java method.
237
         * @param attr the attribute name
238
         * @param clazz the fully-qualified name of the factory class
239
         * @param method the name of a static method
240
         * @return this builder
241
         */
242
        public File methodvalue(String attr, String clazz, String method) {
243
            attrs.put(attr, new String[] {"methodvalue", clazz + "." + method});
244
            return this;
245
        }
246
247
        /**
248
         * Adds an attribute loaded from a Java constructor.
249
         * @param attr the attribute name
250
         * @param clazz the fully-qualified name of a class with a no-argument constructor
251
         * @return this builder
252
         */
253
        public File newvalue(String attr, String clazz) {
254
            attrs.put(attr, new String[] {"newvalue", clazz});
255
            return this;
256
        }
257
258
        /**
259
         * Adds an attribute loaded from a resource bundle.
260
         * @param attr the attribute name
261
         * @param bundle the full name of the bundle, e.g. {@code "org.my.module.Bundle"}
262
         * @param key the key to look up inside the bundle
263
         * @return this builder
264
         */
265
        public File bundlevalue(String attr, String bundle, String key) {
266
            attrs.put(attr, new String[] {"bundlevalue", bundle + "#" + key});
267
            return this;
268
        }
269
270
        // XXX do we want/need serialvalue? passed as String, or byte[], or Object?
271
272
        /**
273
         * Sets a position attribute.
274
         * This is a convenience method so you can define in your annotation:
275
         * <code>int position() default Integer.MAX_VALUE;</code>
276
         * and later call:
277
         * <code>fileBuilder.position(annotation.position())</code>
278
         * @param position a numeric position for this file, or {@link Integer#MAX_VALUE} to not define any position
279
         * @return this builder
280
         */
281
        public File position(int position) {
282
            if (position != Integer.MAX_VALUE) {
283
                intvalue("position", position);
284
            }
285
            return this;
286
        }
287
288
        /**
289
         * Writes the file to the layer.
290
         * Any intervening parent folders are created automatically.
291
         * If the file already exists, the old copy is replaced.
292
         * @return the originating layer builder, in case you want to add another file
293
         */
294
        public LayerBuilder write() {
295
            Element e = doc.getDocumentElement();
296
            String[] pieces = path.split("/");
297
            for (String piece : Arrays.asList(pieces).subList(0, pieces.length - 1)) {
298
                Element kid = find(e, piece);
299
                if (kid != null) {
300
                    if (!kid.getNodeName().equals("folder")) {
301
                        throw new IllegalArgumentException(path);
302
                    }
303
                    e = kid;
304
                } else {
305
                    e = (Element) e.appendChild(doc.createElement("folder"));
306
                    e.setAttribute("name", piece);
307
                }
308
            }
309
            String piece = pieces[pieces.length - 1];
310
            Element file = find(e,piece);
311
            if (file != null) {
312
                e.removeChild(file);
313
            }
314
            file = (Element) e.appendChild(doc.createElement("file"));
315
            file.setAttribute("name", piece);
316
            for (Map.Entry<String,String[]> entry : attrs.entrySet()) {
317
                Element attr = (Element) file.appendChild(doc.createElement("attr"));
318
                attr.setAttribute("name", entry.getKey());
319
                attr.setAttribute(entry.getValue()[0], entry.getValue()[1]);
320
            }
321
            if (url != null) {
322
                file.setAttribute("url", url);
323
            } else if (contents != null) {
324
                file.appendChild(doc.createCDATASection(contents));
325
            }
326
            return LayerBuilder.this;
327
        }
328
329
        private Element find(Element parent, String name) {
330
            NodeList nl = parent.getElementsByTagName("*");
331
            for (int i = 0; i < nl.getLength(); i++) {
332
                Element e = (Element) nl.item(i);
333
                if (e.getAttribute("name").equals(name)) {
334
                    return e;
335
                }
336
            }
337
            return null;
338
        }
339
340
    }
341
342
}
(-)a/openide.filesystems/src/org/openide/filesystems/annotations/LayerGeneratingProcessor.java (+287 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2008 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.filesystems.annotations;
41
42
import java.io.ByteArrayInputStream;
43
import java.io.ByteArrayOutputStream;
44
import java.io.FileNotFoundException;
45
import java.io.IOException;
46
import java.io.InputStream;
47
import java.io.OutputStream;
48
import java.util.ArrayList;
49
import java.util.Arrays;
50
import java.util.List;
51
import java.util.Map;
52
import java.util.Set;
53
import java.util.SortedSet;
54
import java.util.TreeSet;
55
import java.util.WeakHashMap;
56
import javax.annotation.processing.AbstractProcessor;
57
import javax.annotation.processing.ProcessingEnvironment;
58
import javax.annotation.processing.RoundEnvironment;
59
import javax.lang.model.element.Element;
60
import javax.lang.model.element.ExecutableElement;
61
import javax.lang.model.element.Modifier;
62
import javax.lang.model.element.TypeElement;
63
import javax.lang.model.type.TypeMirror;
64
import javax.lang.model.util.ElementFilter;
65
import javax.tools.Diagnostic.Kind;
66
import javax.tools.FileObject;
67
import javax.tools.StandardLocation;
68
import org.openide.filesystems.XMLFileSystem;
69
import org.openide.xml.XMLUtil;
70
import org.w3c.dom.Document;
71
import org.w3c.dom.NodeList;
72
import org.xml.sax.EntityResolver;
73
import org.xml.sax.ErrorHandler;
74
import org.xml.sax.InputSource;
75
import org.xml.sax.SAXException;
76
import org.xml.sax.SAXParseException;
77
78
/**
79
 * Convenience base class for an annotation processor which creates XML layer entries.
80
 * @see XMLFileSystem
81
 */
82
public abstract class LayerGeneratingProcessor extends AbstractProcessor {
83
84
    private static final String GENERATED_LAYER = "META-INF/generated-layer.xml";
85
    private static final String PUBLIC_DTD_ID = "-//NetBeans//DTD Filesystem 1.2//EN";
86
    private static final String NETWORK_DTD_URL = "http://www.netbeans.org/dtds/filesystem-1_2.dtd";
87
    private static final String LOCAL_DTD_RESOURCE = "/org/openide/filesystems/filesystem1_2.dtd";
88
89
    private static final ErrorHandler ERROR_HANDLER = new ErrorHandler() {
90
        public void warning(SAXParseException exception) throws SAXException {throw exception;}
91
        public void error(SAXParseException exception) throws SAXException {throw exception;}
92
        public void fatalError(SAXParseException exception) throws SAXException {throw exception;}
93
    };
94
95
    private static final EntityResolver ENTITY_RESOLVER = new EntityResolver() {
96
        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
97
            if (PUBLIC_DTD_ID.equals(publicId)) {
98
                return new InputSource(LayerGeneratingProcessor.class.getResource(LOCAL_DTD_RESOURCE).toString());
99
            } else {
100
                return null;
101
            }
102
        }
103
    };
104
105
    private static final Map<ProcessingEnvironment,Document> generatedLayerByProcessor = new WeakHashMap<ProcessingEnvironment,Document>();
106
    private static final Map<ProcessingEnvironment,List<Element>> originatingElementsByProcessor = new WeakHashMap<ProcessingEnvironment,List<Element>>();
107
108
    /** For access by subclasses. */
109
    protected LayerGeneratingProcessor() {}
110
111
    @Override
112
    public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
113
        boolean ret = doProcess(annotations, roundEnv);
114
        if (roundEnv.processingOver()) {
115
            Document doc = generatedLayerByProcessor.remove(processingEnv);
116
            if (doc != null) {
117
                Element[] originatingElementsA = new Element[0];
118
                List<Element> originatingElementsL = originatingElementsByProcessor.remove(processingEnv);
119
                if (originatingElementsL != null) {
120
                    originatingElementsA = originatingElementsL.toArray(originatingElementsA);
121
                }
122
                try {
123
                    // Write to memory and reparse to make sure it is valid according to DTD before writing to disk.
124
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
125
                    XMLUtil.write(doc, baos, "UTF-8");
126
                    byte[] data = baos.toByteArray();
127
                    XMLUtil.parse(new InputSource(new ByteArrayInputStream(data)), true, true, ERROR_HANDLER, ENTITY_RESOLVER);
128
                    FileObject layer = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", GENERATED_LAYER, originatingElementsA);
129
                    OutputStream os = layer.openOutputStream();
130
                    try {
131
                        os.write(data);
132
                    } finally {
133
                        os.close();
134
                    }
135
                    {
136
                        SortedSet<String> files = new TreeSet<String>();
137
                        NodeList nl = doc.getElementsByTagName("file");
138
                        for (int i = 0; i < nl.getLength(); i++) {
139
                            org.w3c.dom.Element e = (org.w3c.dom.Element) nl.item(i);
140
                            String name = e.getAttribute("name");
141
                            while ((e = (org.w3c.dom.Element) e.getParentNode()).getTagName().equals("folder")) {
142
                                name = e.getAttribute("name") + "/" + name;
143
                            }
144
                            files.add(name);
145
                        }
146
                        processingEnv.getMessager().printMessage(Kind.NOTE, "generated layer entries: " + files);
147
                    }
148
                } catch (IOException x) {
149
                    processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write generated-layer.xml: " + x.toString());
150
                } catch (SAXException x) {
151
                    processingEnv.getMessager().printMessage(Kind.ERROR, "Refused to write invalid generated-layer.xml: " + x.toString());
152
                }
153
            }
154
        }
155
        return ret;
156
    }
157
158
    /**
159
     * The regular body of {@link #process}.
160
     * In the last round, one of the layer-generating processors will write out generated-layer.xml.
161
     * <p>Do not attempt to read or write the layer file directly; just use {@link #layer}.
162
     * You may however wish to create other resource files yourself: see {@link LayerBuilder.File#url} for syntax.
163
     * @param annotations as in {@link #process}
164
     * @param roundEnv as in {@link #process}
165
     * @return as in {@link #process}
166
     */
167
    protected abstract boolean doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
168
169
    /**
170
     * Access the generated XML layer document.
171
     * May already have content from a previous compilation run which should be overwritten.
172
     * May also have content from other layer-generated processors which should be appended to.
173
     * Simply make changes to the document and they will be written to disk at the end of the job.
174
     * <p>Use {@link LayerBuilder} to easily add file entries without working with the DOM directly.
175
     * @param originatingElements as in {@link Filer#createResource}, optional
176
     * @return the DOM document corresponding to the XML layer being created
177
     */
178
    protected final Document layer(Element... originatingElements) {
179
        List<Element> originatingElementsL = originatingElementsByProcessor.get(processingEnv);
180
        if (originatingElementsL == null) {
181
            originatingElementsL = new ArrayList<Element>();
182
            originatingElementsByProcessor.put(processingEnv, originatingElementsL);
183
        }
184
        originatingElementsL.addAll(Arrays.asList(originatingElements));
185
        Document doc = generatedLayerByProcessor.get(processingEnv);
186
        if (doc == null) {
187
            try {
188
                FileObject layer = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", GENERATED_LAYER);
189
                InputStream is = layer.openInputStream();
190
                try {
191
                    doc = XMLUtil.parse(new InputSource(is), true, true, ERROR_HANDLER, ENTITY_RESOLVER);
192
                } finally {
193
                    is.close();
194
                }
195
            } catch (FileNotFoundException fnfe) {
196
                // Fine, not yet created.
197
            } catch (IOException x) {
198
                processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to read generated-layer.xml: " + x.toString());
199
            } catch (SAXException x) {
200
                processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to parse generated-layer.xml: " + x.toString());
201
            }
202
            if (doc == null) {
203
                doc = XMLUtil.createDocument("filesystem", null, PUBLIC_DTD_ID, NETWORK_DTD_URL);
204
            }
205
            generatedLayerByProcessor.put(processingEnv, doc);
206
        }
207
        return doc;
208
    }
209
210
    /**
211
     * Generate an instance file whose {@code InstanceCookie} would load a given class or method.
212
     * Useful for processors which define layer fragments which instantiate Java objects from the annotated code.
213
     * <p>While you can pick a specific instance file name, if possible you should pass null for {@code name}
214
     * as using the generated name will help avoid accidental name collisions between annotations.
215
     * @param builder a builder to add a file to
216
     * @param annotationTarget an annotated {@linkplain TypeElement class} or {@linkplain ExecutableElement method}
217
     * @param path path to folder of instance file, e.g. {@code "Menu/File"}
218
     * @param name instance file basename, e.g. {@code "my-menu-Item"}, or null to pick a name according to the element
219
     * @param type a type to which the instance ought to be assignable, or null to skip this check
220
     * @return an instance file (call {@link LayerBuilder.File#write} to finalize)
221
     * @throws IllegalArgumentException if the annotationTarget is not of a suitable sort
222
     *                                  (detail message can be reported as a {@link Kind#ERROR})
223
     */
224
    protected final LayerBuilder.File instanceFile(LayerBuilder builder, Element annotationTarget,
225
            String path, String name, Class type) throws IllegalArgumentException {
226
        String clazz, method;
227
        TypeMirror typeMirror = type != null ? processingEnv.getElementUtils().getTypeElement(type.getName()).asType() : null;
228
        switch (annotationTarget.getKind()) {
229
            case CLASS: {
230
                clazz = processingEnv.getElementUtils().getBinaryName((TypeElement) annotationTarget).toString();
231
                method = null;
232
                if (annotationTarget.getModifiers().contains(Modifier.ABSTRACT)) {
233
                    throw new IllegalArgumentException(clazz + " must not be abstract");
234
                }
235
                {
236
                    boolean hasDefaultCtor = false;
237
                    for (ExecutableElement constructor : ElementFilter.constructorsIn(annotationTarget.getEnclosedElements())) {
238
                        if (constructor.getParameters().isEmpty()) {
239
                            hasDefaultCtor = true;
240
                            break;
241
                        }
242
                    }
243
                    if (!hasDefaultCtor) {
244
                        throw new IllegalArgumentException(clazz + " must have a no-argument constructor");
245
                    }
246
                }
247
                if (typeMirror != null && !processingEnv.getTypeUtils().isAssignable(annotationTarget.asType(), typeMirror)) {
248
                    throw new IllegalArgumentException(clazz + " is not assignable to " + typeMirror);
249
                }
250
                break;
251
            }
252
            case METHOD: {
253
                clazz = processingEnv.getElementUtils().getBinaryName((TypeElement) annotationTarget.getEnclosingElement()).toString();
254
                method = annotationTarget.getSimpleName().toString();
255
                if (!annotationTarget.getModifiers().contains(Modifier.STATIC)) {
256
                    throw new IllegalArgumentException(clazz + "." + method + " must be static");
257
                }
258
                if (!((ExecutableElement) annotationTarget).getParameters().isEmpty()) {
259
                    throw new IllegalArgumentException(clazz + "." + method + " must not take arguments");
260
                }
261
                if (typeMirror != null && !processingEnv.getTypeUtils().isAssignable(((ExecutableElement) annotationTarget).getReturnType(), typeMirror)) {
262
                    throw new IllegalArgumentException(clazz + "." + method + " is not assignable to " + typeMirror);
263
                }
264
                break;
265
            }
266
            default:
267
                throw new IllegalArgumentException("Annotated element is not loadable as an instance: " + annotationTarget);
268
        }
269
        String basename;
270
        if (name == null) {
271
            basename = clazz.replace('.', '-');
272
            if (method != null) {
273
                basename += "-" + method;
274
            }
275
        } else {
276
            basename = name;
277
        }
278
        LayerBuilder.File f = builder.file(path + "/" + basename + ".instance");
279
        if (method != null) {
280
            f.methodvalue("instanceCreate", clazz, method);
281
        } else if (name != null) {
282
            f.stringvalue("instanceClass", clazz);
283
        } // else name alone suffices
284
        return f;
285
    }
286
287
}
(-)a/openide.filesystems/test/unit/src/org/openide/filesystems/annotations/LayerBuilderTest.java (+101 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 *
35
 * Contributor(s):
36
 *
37
 * Portions Copyrighted 2008 Sun Microsystems, Inc.
38
 */
39
40
package org.openide.filesystems.annotations;
41
42
import java.io.ByteArrayOutputStream;
43
import java.io.IOException;
44
import org.netbeans.junit.NbTestCase;
45
import org.openide.xml.XMLUtil;
46
import org.w3c.dom.Document;
47
48
public class LayerBuilderTest extends NbTestCase {
49
50
    public LayerBuilderTest(String n) {
51
        super(n);
52
    }
53
54
    private Document doc;
55
    private LayerBuilder b;
56
57
    @Override
58
    protected void setUp() throws Exception {
59
        super.setUp();
60
        doc = XMLUtil.createDocument("filesystem", null, null, null);
61
        b = new LayerBuilder(doc);
62
        assertEquals("<filesystem/>", dump());
63
    }
64
65
    private String dump() throws IOException {
66
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
67
        XMLUtil.write(doc, baos, "UTF-8");
68
        return baos.toString("UTF-8").
69
                replace('"', '\'').
70
                replaceFirst("^<\\?xml version='1\\.0' encoding='UTF-8'\\?>\r?\n", "").
71
                replaceAll("\r?\n *", "");
72
    }
73
74
    public void testBasicFiles() throws Exception {
75
        b.file("Menu/File/x.instance").stringvalue("instanceClass", "some.X").write().
76
                file("Menu/Edit/y.instance").stringvalue("instanceClass", "some.Y").write();
77
        assertEquals("<filesystem><folder name='Menu'>" +
78
                "<folder name='File'><file name='x.instance'><attr name='instanceClass' stringvalue='some.X'/></file></folder>" +
79
                "<folder name='Edit'><file name='y.instance'><attr name='instanceClass' stringvalue='some.Y'/></file></folder>" +
80
                "</folder></filesystem>", dump());
81
    }
82
83
    public void testContent() throws Exception {
84
        b.file("a.txt").contents("some text here...").write().
85
                file("b.xml").url("/resources/b.xml").write();
86
        assertEquals("<filesystem><file name='a.txt'><![CDATA[some text here...]]></file>" +
87
                "<file name='b.xml' url='/resources/b.xml'/></filesystem>", dump());
88
    }
89
90
    public void testOverwriting() throws Exception {
91
        b.file("Menu/File/x.instance").stringvalue("instanceClass", "some.X").write();
92
        assertEquals("<filesystem><folder name='Menu'>" +
93
                "<folder name='File'><file name='x.instance'><attr name='instanceClass' stringvalue='some.X'/></file></folder>" +
94
                "</folder></filesystem>", dump());
95
        b.file("Menu/File/x.instance").write();
96
        assertEquals("<filesystem><folder name='Menu'>" +
97
                "<folder name='File'><file name='x.instance'/></folder>" +
98
                "</folder></filesystem>", dump());
99
    }
100
101
}

Return to bug 149136