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 141925
Collapse All | Expand All

(-)a/core.ide/src/org/netbeans/core/ide/resources/layer.xml (+1 lines)
Lines 92-97 Link Here
92
             the changes if appropriate -->
92
             the changes if appropriate -->
93
        <file name="Standard.xml" url="Standard.xml">
93
        <file name="Standard.xml" url="Standard.xml">
94
            <attr name="position" intvalue="800"/>
94
            <attr name="position" intvalue="800"/>
95
            <attr name="weight" intvalue="100"/><!-- override core.ui -->
95
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.core.ide.resources.Bundle"/>
96
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.core.ide.resources.Bundle"/>
96
        </file>
97
        </file>
97
        <file name="Debugging.xml" url="Debugging.xml">
98
        <file name="Debugging.xml" url="Debugging.xml">
(-)a/favorites/src/org/netbeans/modules/favorites/resources/layer.xml (-7 lines)
Lines 135-147 Link Here
135
                <attr name="templateCategory" stringvalue="simple-files"/>
135
                <attr name="templateCategory" stringvalue="simple-files"/>
136
            </file>
136
            </file>
137
        </folder>
137
        </folder>
138
        <!-- folder for all privileged templates -->
139
        <folder name="Privileged">
140
	    <file name="Folder.shadow">
141
                <attr name="originalFile" stringvalue="Templates/Other/Folder"/>
142
                <attr name="position" intvalue="2000"/>
143
            </file>
144
	</folder>
145
        <!-- file with default template for properties -->
138
        <!-- file with default template for properties -->
146
        <folder name="Properties">
139
        <folder name="Properties">
147
            <attr name="position" intvalue="2900"/>
140
            <attr name="position" intvalue="2900"/>
(-)a/j2me.cdc.project.savaje/src/org/netbeans/modules/j2me/cdc/project/savaje/resources/cdc-hi-init (-9 lines)
Lines 1-9 Link Here
1
<xsl:comment>cdc-hi-init</xsl:comment>
2
3
    <target name="cdc-hi-pre-init" if="cdc-platform.trigger">
4
        <condition property="cdc-hi-platform.trigger">
5
            <equals arg1="cdc-hi" arg2="${{platform.type}}" casesensitive="false"/>
6
        </condition>
7
    </target>
8
    <target name="cdc-hi-init" depends="cdc-hi-pre-init" if="cdc-hi-platform.trigger"/>
9
    
(-)a/j2me.cdc.project.savaje/src/org/netbeans/modules/j2me/cdc/project/savaje/resources/cdc-hi-run (-18 lines)
Lines 1-18 Link Here
1
<xsl:comment>cdc-hi-run</xsl:comment>
2
3
    <target name="-pre-cdc-hi-run" if="cdc-hi-platform.trigger"/>
4
5
    <target name="-cdc-hi-run" if="cdc-hi-platform.trigger">
6
        <exec executable="${{platform.home}}/bin/emulator">
7
          <arg value="-cp"/>
8
          <arg value="${{basedir}}/${{dist.dir}}/${{dist.jar}}"/>
9
          <arg value="-Xdevice:${{platform.device}}"/>
10
          <arg value="${{run.cmd.options}}"/>
11
          <arg value="-Xmain:${{main.class}}"/>
12
          <arg value="${{application.args}}"/>
13
        </exec>
14
    </target>
15
16
    <target name="-post-cdc-hi-run" if="cdc-hi-platform.trigger"/>
17
18
    <target name="cdc-hi-run" if="cdc-hi-platform.trigger" depends="-pre-cdc-hi-run, -cdc-hi-run, -post-cdc-hi-run"/>
(-)a/j2me.cdc.project.savaje/src/org/netbeans/modules/j2me/cdc/project/savaje/resources/layer.xml (-6 lines)
Lines 9-17 Link Here
9
                        <file name="savaje-init" url="savaje-init">
9
                        <file name="savaje-init" url="savaje-init">
10
                            <attr name="position" intvalue="700"/>
10
                            <attr name="position" intvalue="700"/>
11
                        </file>
11
                        </file>
12
                        <file name="cdc-hi-init" url="cdc-hi-init">
13
                            <attr name="position" intvalue="750"/>
14
                        </file>
15
                    </folder>
12
                    </folder>
16
                    <folder name="jar-subtargets">
13
                    <folder name="jar-subtargets">
17
                        <file name="savaje-build-jnlp" url="savaje-build-jnlp">
14
                        <file name="savaje-build-jnlp" url="savaje-build-jnlp">
Lines 22-30 Link Here
22
                        <file name="savaje-run" url="savaje-run">
19
                        <file name="savaje-run" url="savaje-run">
23
                            <attr name="position" intvalue="400"/>
20
                            <attr name="position" intvalue="400"/>
24
                        </file>
21
                        </file>
25
                        <file name="cdc-hi-run" url="cdc-hi-run">
26
                            <attr name="position" intvalue="975"/>
27
                        </file>
28
                    </folder>
22
                    </folder>
29
                    <folder name="debug-subtargets">
23
                    <folder name="debug-subtargets">
30
                        <file name="savaje-debug" url="savaje-debug">
24
                        <file name="savaje-debug" url="savaje-debug">
(-)a/mobility.project/src/org/netbeans/modules/mobility/project/ui/resources/buildscript/cdc-hi-init (-2 / +1 lines)
Lines 5-9 Link Here
5
            <equals arg1="cdc-hi" arg2="${{platform.type}}" casesensitive="false"/>
5
            <equals arg1="cdc-hi" arg2="${{platform.type}}" casesensitive="false"/>
6
        </condition>
6
        </condition>
7
    </target>
7
    </target>
8
    <target name="cdc-hi-init" depends="cdc-hi-pre-init" if="cdc-hi-platform.trigger">
8
    <target name="cdc-hi-init" depends="cdc-hi-pre-init" if="cdc-hi-platform.trigger"/>
9
    </target>
(-)a/mobility.project/src/org/netbeans/modules/mobility/project/ui/resources/buildscript/cdc-hi-run (-1 lines)
Lines 16-19 Link Here
16
    <target name="-post-cdc-hi-run" if="cdc-hi-platform.trigger"/>
16
    <target name="-post-cdc-hi-run" if="cdc-hi-platform.trigger"/>
17
17
18
    <target name="cdc-hi-run" if="cdc-hi-platform.trigger" depends="-pre-cdc-hi-run, -cdc-hi-run, -post-cdc-hi-run"/>
18
    <target name="cdc-hi-run" if="cdc-hi-platform.trigger" depends="-pre-cdc-hi-run, -cdc-hi-run, -post-cdc-hi-run"/>
19
	
(-)a/mobility.project/src/org/netbeans/modules/mobility/project/ui/resources/layer.xml (-2 / +2 lines)
Lines 364-370 Link Here
364
                        </file>
364
                        </file>
365
                        <!-- Java ME SDK beginning -->
365
                        <!-- Java ME SDK beginning -->
366
                        <file name="cdc-hi-init" url="buildscript/cdc-hi-init">
366
                        <file name="cdc-hi-init" url="buildscript/cdc-hi-init">
367
                            <attr name="position" intvalue="1000"/>
367
                            <attr name="position" intvalue="750"/>
368
                        </file>
368
                        </file>
369
                        <!-- Java ME SDK end -->
369
                        <!-- Java ME SDK end -->
370
                    </folder>
370
                    </folder>
Lines 466-472 Link Here
466
                            <attr name="position" intvalue="0"/>
466
                            <attr name="position" intvalue="0"/>
467
                        </file>
467
                        </file>
468
                        <file name="cdc-hi-run" url="buildscript/cdc-hi-run">
468
                        <file name="cdc-hi-run" url="buildscript/cdc-hi-run">
469
                            <attr name="position" intvalue="1000"/>
469
                            <attr name="position" intvalue="975"/>
470
                        </file>
470
                        </file>
471
                        <file name="open-profiler" url="buildscript/open-profiler">
471
                        <file name="open-profiler" url="buildscript/open-profiler">
472
                            <attr name="position" intvalue="1200"/>
472
                            <attr name="position" intvalue="1200"/>
(-)a/o.n.core/test/qa-functional/src/org/netbeans/core/validation/ValidateLayerConsistencyTest.java (-99 / +51 lines)
Lines 86-92 Link Here
86
import org.openide.loaders.DataObject;
86
import org.openide.loaders.DataObject;
87
import org.openide.loaders.DataShadow;
87
import org.openide.loaders.DataShadow;
88
import org.openide.modules.Dependency;
88
import org.openide.modules.Dependency;
89
import org.openide.modules.ModuleInfo;
90
import org.openide.util.Lookup;
89
import org.openide.util.Lookup;
91
import org.openide.util.Mutex;
90
import org.openide.util.Mutex;
92
import org.openide.util.NbCollections;
91
import org.openide.util.NbCollections;
Lines 477-488 Link Here
477
        assertNoErrors(errors.size() + " actions is not registered properly", errors);
476
        assertNoErrors(errors.size() + " actions is not registered properly", errors);
478
    }
477
    }
479
    
478
    
480
    public void testIfOneFileIsDefinedTwiceByDifferentModulesTheyNeedToHaveMutualDependency() throws Exception {
479
    public void testLayerOverrides() throws Exception {
481
        ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
480
        ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
482
        assertNotNull ("In the IDE mode, there always should be a classloader", l);
481
        assertNotNull ("In the IDE mode, there always should be a classloader", l);
483
        
482
        
484
        // String -> List<Modules>
485
        Map<String,List<String>> files = new HashMap<String,List<String>>();
486
        class ContentAndAttrs {
483
        class ContentAndAttrs {
487
            final byte[] contents;
484
            final byte[] contents;
488
            final Map<String,Object> attrs;
485
            final Map<String,Object> attrs;
Lines 506-524 Link Here
506
                return Arrays.equals(contents, caa.contents) && attrs.equals(caa.attrs);
503
                return Arrays.equals(contents, caa.contents) && attrs.equals(caa.attrs);
507
            }
504
            }
508
        }
505
        }
509
        /* < FO path , { content, attributes } > */
506
        Map</* path */String,Map</* owner */String,ContentAndAttrs>> files = new TreeMap<String,Map<String,ContentAndAttrs>>();
510
        Map<String,ContentAndAttrs> contents = new HashMap<String,ContentAndAttrs>();
511
        /* < FO path , < module name, { content, attributes } > > */
512
        Map<String,Map<String,ContentAndAttrs>> differentContents = new HashMap<String,Map<String,ContentAndAttrs>>();
513
        Map</* path */String,Map</* attr name */String,Map</* module name */String,/* attr value */Object>>> folderAttributes =
507
        Map</* path */String,Map</* attr name */String,Map</* module name */String,/* attr value */Object>>> folderAttributes =
514
                new TreeMap<String,Map<String,Map<String,Object>>>();
508
                new TreeMap<String,Map<String,Map<String,Object>>>();
509
        Map<String,Set<String>> directDeps = new HashMap<String,Set<String>>();
515
        StringBuffer sb = new StringBuffer();
510
        StringBuffer sb = new StringBuffer();
516
        Map<String,URL> hiddenFiles = new HashMap<String, URL>();
511
        Map<String,URL> hiddenFiles = new HashMap<String, URL>();
517
        Set<String> allFiles = new HashSet<String>();
512
        Set<String> allFiles = new HashSet<String>();
518
        final String suffix = "_hidden";
513
        final String suffix = "_hidden";
519
514
520
        
521
        boolean atLeastOne = false;
522
        Enumeration<URL> en = l.getResources("META-INF/MANIFEST.MF");
515
        Enumeration<URL> en = l.getResources("META-INF/MANIFEST.MF");
523
        while (en.hasMoreElements ()) {
516
        while (en.hasMoreElements ()) {
524
            URL u = en.nextElement();
517
            URL u = en.nextElement();
Lines 535-569 Link Here
535
            }
528
            }
536
            String layer = mf.getMainAttributes ().getValue ("OpenIDE-Module-Layer");
529
            String layer = mf.getMainAttributes ().getValue ("OpenIDE-Module-Layer");
537
            if (layer == null) {
530
            if (layer == null) {
531
                // XXX should also consider META-INF/generated-layer.xml here
538
                continue;
532
                continue;
539
            }
533
            }
534
            String depsS = mf.getMainAttributes().getValue("OpenIDE-Module-Module-Dependencies");
535
            if (depsS != null) {
536
                Set<String> deps = new HashSet<String>();
537
                for (Dependency d : Dependency.create(Dependency.TYPE_MODULE, depsS)) {
538
                    deps.add(d.getName().replaceFirst("/.+$", ""));
539
                }
540
                directDeps.put(module, deps);
541
            }
540
            
542
            
541
            atLeastOne = true;
542
            URL base = new URL(u, "../");
543
            URL base = new URL(u, "../");
543
            URL layerURL = new URL(base, layer);
544
            URL layerURL = new URL(base, layer);
544
            java.net.URLConnection connect = layerURL.openConnection ();
545
            URLConnection connect = layerURL.openConnection ();
545
            connect.setDefaultUseCaches (false);
546
            connect.setDefaultUseCaches (false);
546
            FileSystem fs = new XMLFileSystem(layerURL);
547
            FileSystem fs = new XMLFileSystem(layerURL);
547
548
548
            Enumeration<? extends FileObject> all = fs.getRoot().getChildren(true);
549
            Enumeration<? extends FileObject> all = fs.getRoot().getChildren(true);
549
            while (all.hasMoreElements ()) {
550
            while (all.hasMoreElements ()) {
550
                FileObject fo = all.nextElement ();
551
                FileObject fo = all.nextElement ();
551
                String path = fo.getPath();
552
                String simplePath = fo.getPath();
552
553
553
                if (path.endsWith(suffix)) {
554
                if (simplePath.endsWith(suffix)) {
554
                    hiddenFiles.put(path, layerURL);
555
                    hiddenFiles.put(simplePath, layerURL);
555
                } else {
556
                } else {
556
                    allFiles.add(path);
557
                    allFiles.add(simplePath);
557
                }
558
                }
558
559
560
                Number weight = (Number) fo.getAttribute("weight");
561
                // XXX if weight != null, test that it is actually overriding something or being overridden
562
                String weightedPath = weight == null ? simplePath : simplePath + "#" + weight;
563
559
                Map<String,Object> attributes = getAttributes(fo, base);
564
                Map<String,Object> attributes = getAttributes(fo, base);
560
565
561
                if (fo.isFolder()) {
566
                if (fo.isFolder()) {
562
                    for (Map.Entry<String,Object> attr : attributes.entrySet()) {
567
                    for (Map.Entry<String,Object> attr : attributes.entrySet()) {
563
                        Map<String,Map<String,Object>> m1 = folderAttributes.get(path);
568
                        Map<String,Map<String,Object>> m1 = folderAttributes.get(weightedPath);
564
                        if (m1 == null) {
569
                        if (m1 == null) {
565
                            m1 = new TreeMap<String,Map<String,Object>>();
570
                            m1 = new TreeMap<String,Map<String,Object>>();
566
                            folderAttributes.put(path, m1);
571
                            folderAttributes.put(weightedPath, m1);
567
                        }
572
                        }
568
                        Map<String,Object> m2 = m1.get(attr.getKey());
573
                        Map<String,Object> m2 = m1.get(attr.getKey());
569
                        if (m2 == null) {
574
                        if (m2 == null) {
Lines 575-677 Link Here
575
                    continue;
580
                    continue;
576
                }
581
                }
577
                
582
                
578
                List<String> list = files.get(path);
583
                Map<String,ContentAndAttrs> overrides = files.get(weightedPath);
579
                if (list == null) {
584
                if (overrides == null) {
580
                    list = new ArrayList<String>();
585
                    overrides = new TreeMap<String,ContentAndAttrs>();
581
                    files.put (path, list);
586
                    files.put(weightedPath, overrides);
582
                    list.add (module);
583
                    contents.put(path, new ContentAndAttrs(fo.asBytes(), attributes, layerURL));
584
                } else {
585
                    ContentAndAttrs contentAttrs = contents.get(path);
586
                    ContentAndAttrs nue = new ContentAndAttrs(fo.asBytes(), attributes, layerURL);
587
                    if (!nue.equals(contentAttrs)) {
588
                        //System.err.println("Found differences in " + path + " between " + nue + " and " + contentAttrs);
589
                        Map<String,ContentAndAttrs> diffs = differentContents.get(path);
590
                        if (diffs == null) {
591
                            diffs = new HashMap<String,ContentAndAttrs>();
592
                            differentContents.put(path, diffs);
593
                            diffs.put(list.get(0), contentAttrs);
594
                        }
595
                        diffs.put(module, nue);
596
                        list.add (module);
597
                    }
598
                }
587
                }
588
                overrides.put(module, new ContentAndAttrs(fo.asBytes(), attributes, layerURL));
599
            }
589
            }
600
            // make sure the filesystem closes the stream
590
            // make sure the filesystem closes the stream
601
            connect.getInputStream ().close ();
591
            connect.getInputStream ().close ();
602
        }
592
        }
603
        contents = null; // Not needed any more
593
        assertFalse("At least one layer file is usually used", allFiles.isEmpty());
604
        
594
605
        for (Map.Entry<String,List<String>> e : files.entrySet()) {
595
        for (Map.Entry<String,Map<String,ContentAndAttrs>> e : files.entrySet()) {
606
            List<String> list = e.getValue();
596
            Map<String,ContentAndAttrs> overrides = e.getValue();
607
            if (list.size() == 1) {
597
            if (overrides.size() == 1) {
608
                continue;
598
                continue;
609
            }
599
            }
610
            
600
            Set<String> overriders = overrides.keySet();
611
            Collection<? extends ModuleInfo> res = Lookup.getDefault().lookupAll(ModuleInfo.class);
601
            String file = e.getKey();
612
            assertFalse("Some modules found", res.isEmpty());
602
613
            
603
            if (new HashSet<ContentAndAttrs>(overrides.values()).size() == 1) {
614
            List<String> list2 = new ArrayList<String>(list);
604
                // All the same. Check whether these are parallel declarations (e.g. CND debugger vs. Java debugger), or vertical.
615
            for (String name : list) {
605
                for (String overrider : overriders) {
616
                for (ModuleInfo info : res) {
606
                    Set<String> deps = new HashSet<String>(directDeps.get(overrider));
617
                    if (name.equals (info.getCodeName ())) {
607
                    deps.retainAll(overriders);
618
                        // remove dependencies
608
                    if (!deps.isEmpty()) {
619
                        for (Dependency d : info.getDependencies()) {
609
                        sb.append(file).append(" is pointlessly overridden in ").append(overrider).
620
                            list2.remove(d.getName());
610
                                append(" relative to ").append(deps.iterator().next()).append('\n');
621
                        }
622
                    }
611
                    }
623
                }
612
                }
624
            }
625
            // ok, modules depend on each other
626
            if (list2.size() <= 1) {
627
                continue;
613
                continue;
628
            }
614
            }
629
            
615
630
            sb.append (e.getKey ()).append( " is provided by: " ).append(list).append('\n');
616
            sb.append(file).append(" is provided by: ").append(overriders).append('\n');
631
            Map<String,ContentAndAttrs> diffList = differentContents.get(e.getKey());
617
            for (Map.Entry<String,ContentAndAttrs> entry : overrides.entrySet()) {
632
            if (diffList != null) {
618
                ContentAndAttrs contentAttrs = entry.getValue();
633
                if (list.size() == 2) {
619
                sb.append(" ").append(entry.getKey()).append(": content = '").append(new String(contentAttrs.contents)).
634
                    String module1 = list.get(0);
620
                        append("', attributes = ").append(contentAttrs.attrs).append("\n");
635
                    String module2 = list.get(1);
636
                    ContentAndAttrs contentAttrs1 = diffList.get(module1);
637
                    ContentAndAttrs contentAttrs2 = diffList.get(module2);
638
                    if (!Arrays.equals(contentAttrs1.contents, contentAttrs2.contents)) {
639
                        sb.append(' ').append(module1).append(": content = '").append(new String(contentAttrs1.contents)).append('\n');
640
                        sb.append(' ').append(module2).append(": content = '").append(new String(contentAttrs2.contents)).append('\n');
641
                    }
642
                    if (!contentAttrs1.attrs.equals(contentAttrs2.attrs)) {
643
                        Map<String,Object> attr1 = contentAttrs1.attrs;
644
                        Map<String,Object> attr2 = contentAttrs2.attrs;
645
                        Set<String> keys = new HashSet<String>(attr1.keySet());
646
                        keys.retainAll(attr2.keySet());
647
                        for (String attribute : keys) {
648
                            Object value1 = attr1.get(attribute);
649
                            Object value2 = attr2.get(attribute);
650
                            if (value1 == value2 || (value1 != null && value1.equals(value2))) {
651
                                // Remove the common attributes so that just the differences show up
652
                                attr1.remove(attribute);
653
                                attr2.remove(attribute);
654
                            }
655
                        }
656
                        sb.append(' ').append(module1).append(": different attributes = '").append(contentAttrs1.attrs).append('\n');
657
                        sb.append(' ').append(module2).append(": different attributes = '").append(contentAttrs2.attrs).append('\n');
658
                    }
659
                } else {
660
                    for (String module : list) {
661
                        ContentAndAttrs contentAttrs = diffList.get(module);
662
                        sb.append(" ").append(module).append(": content = '").append(new String(contentAttrs.contents)).
663
                                append("', attributes = ").append(contentAttrs.attrs).append("\n");
664
                    }
665
                }
666
            }
621
            }
667
        }        
622
        }        
668
        
623
        
669
        assertTrue ("At least one layer file is usually used", atLeastOne);
670
        
671
        for (Map.Entry<String,Map<String,Map<String,Object>>> entry1 : folderAttributes.entrySet()) {
624
        for (Map.Entry<String,Map<String,Map<String,Object>>> entry1 : folderAttributes.entrySet()) {
672
            for (Map.Entry<String,Map<String,Object>> entry2 : entry1.getValue().entrySet()) {
625
            for (Map.Entry<String,Map<String,Object>> entry2 : entry1.getValue().entrySet()) {
673
                if (new HashSet<Object>(entry2.getValue().values()).size() > 1) {
626
                if (new HashSet<Object>(entry2.getValue().values()).size() > 1) {
674
                    // XXX currently do not check if the modules are unrelated by dependency.
675
                    sb.append("Some modules conflict on the definition of ").append(entry2.getKey()).append(" for ").
627
                    sb.append("Some modules conflict on the definition of ").append(entry2.getKey()).append(" for ").
676
                            append(entry1.getKey()).append(": ").append(entry2.getValue()).append("\n");
628
                            append(entry1.getKey()).append(": ").append(entry2.getValue()).append("\n");
677
                }
629
                }
Lines 679-685 Link Here
679
        }
631
        }
680
632
681
        if (sb.length () > 0) {
633
        if (sb.length () > 0) {
682
            fail ("Some modules override their files and do not depend on each other\n" + sb);
634
            fail("Some modules override some files without using the weight attribute correctly\n" + sb);
683
        }
635
        }
684
636
685
637
(-)a/openide.filesystems/apichanges.xml (+54 lines)
Lines 46-51 Link Here
46
        <apidef name="filesystems">Filesystems API</apidef>
46
        <apidef name="filesystems">Filesystems API</apidef>
47
    </apidefs>
47
    </apidefs>
48
    <changes>
48
    <changes>
49
        <change id="MultiFileSystem.weight">
50
            <api name="filesystems"/>
51
            <summary>Ability to specify "weight" for <code>MultiFileSystem</code> overrides</summary>
52
            <version major="7" minor="36"/>
53
            <date day="1" month="3" year="2010"/>
54
            <author login="jglick"/>
55
            <compatibility semantic="compatible">
56
                <p>
57
                    Modules need no longer declare runtime-only dependencies
58
                    on other modules merely to override their layer entries.
59
                    Instead, they should specify a higher weight.
60
                    (Normally the base module will declare no weight, so any
61
                    positive number will do.)
62
                </p>
63
            </compatibility>
64
            <description>
65
                <p>
66
                    <code>MultiFileSystem</code>s will now interpret a special
67
                    file attribute <code>weight</code> to determine how to
68
                    resolve otherwise ambiguous overrides. For example, in the
69
                    system filesystem, module XML layers can use this attribute.
70
                    Suppose a generic infrastructure module declares:
71
                </p>
72
                <pre>
73
&lt;filesystem>
74
    &lt;folder name="my-snippets">
75
        &lt;file name="common.xml" url="generic-snippet.xml"/>
76
    &lt;/folder>
77
&lt;/filesystem>
78
                </pre>
79
                <p>
80
                    If another module wishes to override this declaration, it
81
                    can do so by specifying:
82
                </p>
83
                <pre>
84
&lt;filesystem>
85
    &lt;folder name="my-snippets">
86
        &lt;file name="common.xml" url="special-snippet.xml">
87
            &lt;attribute name="weight" intvalue="100"/>
88
        &lt;/file>
89
    &lt;/folder>
90
&lt;/filesystem>
91
                </pre>
92
                <p>
93
                    This override will work even if the module system happens
94
                    to load the specializing module <em>before</em> the infrastructure
95
                    module (as could happen if there is no module dependency
96
                    between them). The weight attribute also makes it clear that
97
                    something is being overridden.
98
                </p>
99
            </description>
100
            <class package="org.openide.filesystems" name="MultiFileSystem"/>
101
            <issue number="141925"/>
102
        </change>
49
           <change id="JarFileSystem-constructor-file">
103
           <change id="JarFileSystem-constructor-file">
50
              <api name="filesystems"/>
104
              <api name="filesystems"/>
51
              <summary>Effective constructor for JarFileSystem</summary>
105
              <summary>Effective constructor for JarFileSystem</summary>
(-)a/openide.filesystems/manifest.mf (-1 / +1 lines)
Lines 2-6 Link Here
2
OpenIDE-Module: org.openide.filesystems
2
OpenIDE-Module: org.openide.filesystems
3
OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/filesystems/Bundle.properties
4
OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml
4
OpenIDE-Module-Layer: org/openide/filesystems/resources/layer.xml
5
OpenIDE-Module-Specification-Version: 7.35
5
OpenIDE-Module-Specification-Version: 7.36
6
6
(-)a/openide.filesystems/src/org/openide/filesystems/MultiFileObject.java (-7 / +57 lines)
Lines 60-65 Link Here
60
import java.util.Properties;
60
import java.util.Properties;
61
import java.util.Set;
61
import java.util.Set;
62
import java.util.WeakHashMap;
62
import java.util.WeakHashMap;
63
import java.util.logging.Level;
64
import java.util.logging.Logger;
63
import org.openide.util.NbBundle;
65
import org.openide.util.NbBundle;
64
66
65
/** Implementation of the file object for multi file system.
67
/** Implementation of the file object for multi file system.
Lines 75-80 Link Here
75
77
76
    /** default path separator */
78
    /** default path separator */
77
    private static final char PATH_SEP = '/';
79
    private static final char PATH_SEP = '/';
80
    private static final String WEIGHT_ATTRIBUTE = "weight"; // NOI18N
78
    private static final FileSystem.AtomicAction markAtomicAction = new FileSystem.AtomicAction() {
81
    private static final FileSystem.AtomicAction markAtomicAction = new FileSystem.AtomicAction() {
79
            public void run() {
82
            public void run() {
80
            }
83
            }
Lines 168-176 Link Here
168
171
169
        Set now = (delegates == null) ? Collections.EMPTY_SET : delegates;
172
        Set now = (delegates == null) ? Collections.EMPTY_SET : delegates;
170
        Set<FileObject> del = new HashSet<FileObject>(arr.length * 2);
173
        Set<FileObject> del = new HashSet<FileObject>(arr.length * 2);
174
        Number maxWeight = 0;
171
        FileObject led = null;
175
        FileObject led = null;
172
176
173
        String name = getPath();
177
        String name = getPath();
178
        FileSystem writable = mfs.writableLayer(name);
174
179
175
        for (int i = 0; i < arr.length; i++) {
180
        for (int i = 0; i < arr.length; i++) {
176
            if (arr[i] != null) {
181
            if (arr[i] != null) {
Lines 184-191 Link Here
184
                        fo.addFileChangeListener(weakL);
189
                        fo.addFileChangeListener(weakL);
185
                    }
190
                    }
186
191
187
                    if ((led == null) && fo.isValid()) {
192
                    if (fo.isValid()) {
188
                        led = fo;
193
                        Number weight = weightOf(fo, writable);
194
                        if (led == null || weight.doubleValue() > maxWeight.doubleValue()) {
195
                            led = fo;
196
                            maxWeight = weight;
197
                        }
189
                    }
198
                    }
190
                }
199
                }
191
            }
200
            }
Lines 297-302 Link Here
297
    private FileObject findLeader(FileSystem[] fs, String path) {
306
    private FileObject findLeader(FileSystem[] fs, String path) {
298
        MultiFileSystem mfs = getMultiFileSystem();
307
        MultiFileSystem mfs = getMultiFileSystem();
299
308
309
        Number maxWeight = 0;
310
        FileObject _leader = null;
311
        FileSystem writable = mfs.writableLayer(path);
312
        
300
        for (FileSystem f : fs) {
313
        for (FileSystem f : fs) {
301
            if (f == null) {
314
            if (f == null) {
302
                continue;
315
                continue;
Lines 304-314 Link Here
304
            FileObject fo = mfs.findResourceOn(f, path);
317
            FileObject fo = mfs.findResourceOn(f, path);
305
318
306
            if (fo != null) {
319
            if (fo != null) {
307
                return fo;
320
                Number weight = weightOf(fo, writable);
321
                if (_leader == null || weight.doubleValue() > maxWeight.doubleValue()) {
322
                    _leader = fo;
323
                    maxWeight = weight;
324
                }
308
            }
325
            }
309
        }
326
        }
310
327
311
        return null;
328
        return _leader;
329
    }
330
331
    private static Number weightOf(FileObject f, FileSystem writable) {
332
        try {
333
            if (f.getFileSystem() == writable) {
334
                return Double.MAX_VALUE;
335
            }
336
        } catch (FileStateInvalidException x) {/* ignore */}
337
        Object weight = f.getAttribute(WEIGHT_ATTRIBUTE);
338
        if (weight instanceof Number) {
339
            return (Number) weight;
340
        } else if (weight == null) {
341
            return 0;
342
        } else {
343
            try {
344
                Logger.getLogger(MultiFileObject.class.getName()).log(
345
                        Level.WARNING, "File {0} in {1} has nonnumeric weight {2} of type {3}",
346
                        new Object[] {f.getPath(), f.getFileSystem(), weight, weight.getClass().getName()});
347
            } catch (FileStateInvalidException x) {/* ignore */}
348
            return 0;
349
        }
312
    }
350
    }
313
351
314
    /** Getter for the right file system */
352
    /** Getter for the right file system */
Lines 774-779 Link Here
774
812
775
        FileSystem[] systems = getMultiFileSystem().getDelegates();
813
        FileSystem[] systems = getMultiFileSystem().getDelegates();
776
814
815
        Number maxWeight = 0;
816
        Object attr = null;
817
        FileSystem writable = getMultiFileSystem().writableLayer(path);
818
777
        //        boolean isLoaderAttr = /* DataObject.EA_ASSIGNED_LOADER */ "NetBeansAttrAssignedLoader".equals (attrName); // NOI18N                
819
        //        boolean isLoaderAttr = /* DataObject.EA_ASSIGNED_LOADER */ "NetBeansAttrAssignedLoader".equals (attrName); // NOI18N                
778
        for (int i = 0; i < systems.length; i++) {
820
        for (int i = 0; i < systems.length; i++) {
779
            if (systems[i] == null) {
821
            if (systems[i] == null) {
Lines 793-799 Link Here
793
                Object o = getAttribute(fo, attrName, fo.getPath()); // Performance tricks:                
835
                Object o = getAttribute(fo, attrName, fo.getPath()); // Performance tricks:                
794
836
795
                if (o != null) {
837
                if (o != null) {
796
                    return devoidify(o);
838
                    Number weight = weightOf(fo, writable);
839
                    if (attr == null || weight.doubleValue() > maxWeight.doubleValue()) {
840
                        attr = o;
841
                        maxWeight = weight;
842
                    }
797
                }
843
                }
798
            }
844
            }
799
845
Lines 807-818 Link Here
807
                Object o = getAttribute(fo, prefixattr, ""); // NOI18N
853
                Object o = getAttribute(fo, prefixattr, ""); // NOI18N
808
854
809
                if (o != null) {
855
                if (o != null) {
810
                    return devoidify(o);
856
                    Number weight = weightOf(fo, writable);
857
                    if (attr == null || weight.doubleValue() > maxWeight.doubleValue()) {
858
                        attr = o;
859
                        maxWeight = weight;
860
                    }
811
                }
861
                }
812
            }
862
            }
813
        }
863
        }
814
864
815
        return null;
865
        return devoidify(attr);
816
    }
866
    }
817
867
818
    private static boolean sameFullName(FileObject f1, FileObject f2) {
868
    private static boolean sameFullName(FileObject f1, FileObject f2) {
(-)a/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java (-2 / +21 lines)
Lines 61-70 Link Here
61
 *
61
 *
62
 * <p>This filesystem has no form of storage in and of itself. Rather, it composes and proxies one or more
62
 * <p>This filesystem has no form of storage in and of itself. Rather, it composes and proxies one or more
63
 * "delegate" filesystems. The result is that of a "layered" sandwich of filesystems, each able to provide files
63
 * "delegate" filesystems. The result is that of a "layered" sandwich of filesystems, each able to provide files
64
 * to appear in the merged result. The layers are ordered so that a filesystem in "front" can override one in
64
 * to appear in the merged result.
65
 * "back". Often the frontmost layer will be writable, and all changes to the filesystem are sent to this layer,
65
 * Often the frontmost layer will be writable, and all changes to the filesystem are sent to this layer,
66
 * but that behavior is configurable.
66
 * but that behavior is configurable.
67
 *
67
 *
68
 * <p>The layers are ordered so that entries in a filesystem in "front" can override one in "back".
69
 * Since it is often not straightforward to arrange layers so that particular overrides work,
70
 * as of org.openide.filesystems 7.36
71
 * you may set the special file attribute {@code weight} to any {@link Number} on a layer entry.
72
 * (If unspecified, the implicit default value is zero.)
73
 * A variant with a higher weight will override one with a lower weight even if it is further back.
74
 * (The exception is that entries in a writable frontmost layer always override other layers,
75
 * regardless of weight.)
76
 *
68
 * <p>Creating a new <code>MultiFileSystem</code> is easy in the simplest cases: just call {@link
77
 * <p>Creating a new <code>MultiFileSystem</code> is easy in the simplest cases: just call {@link
69
 * #MultiFileSystem(FileSystem[])} and pass a list of delegates. If you pass it only read-only delegates, the
78
 * #MultiFileSystem(FileSystem[])} and pass a list of delegates. If you pass it only read-only delegates, the
70
 * composite will also be read-only. Or you may pass it one or more writable filesystems (make sure the first
79
 * composite will also be read-only. Or you may pass it one or more writable filesystems (make sure the first
Lines 448-453 Link Here
448
        return systems[WRITE_SYSTEM_INDEX];
457
        return systems[WRITE_SYSTEM_INDEX];
449
    }
458
    }
450
459
460
    FileSystem writableLayer(String path) {
461
        try {
462
            return createWritableOn(path);
463
        } catch (IOException x) {
464
            // ignore
465
            return systems.length > WRITE_SYSTEM_INDEX ? systems[WRITE_SYSTEM_INDEX] : null;
466
        }
467
    }
468
451
    /** Special case of createWritableOn (@see #createWritableOn).
469
    /** Special case of createWritableOn (@see #createWritableOn).
452
    *
470
    *
453
    * @param oldName original name of the file (full)
471
    * @param oldName original name of the file (full)
Lines 555-560 Link Here
555
    Enumeration<FileObject> delegates(final String name) {
573
    Enumeration<FileObject> delegates(final String name) {
556
        Enumeration<FileSystem> en = Enumerations.array(systems);
574
        Enumeration<FileSystem> en = Enumerations.array(systems);
557
575
576
        // XXX order (stably) by weight
558
        class Resources implements Enumerations.Processor<FileSystem, FileObject> {
577
        class Resources implements Enumerations.Processor<FileSystem, FileObject> {
559
            public @Override FileObject process(FileSystem fs, Collection<FileSystem> ignore) {
578
            public @Override FileObject process(FileSystem fs, Collection<FileSystem> ignore) {
560
                if (fs == null) {
579
                if (fs == null) {
(-)a/openide.filesystems/test/unit/src/org/openide/filesystems/MultiFileSystemMaskTest.java (+60 lines)
Lines 41-51 Link Here
41
41
42
package org.openide.filesystems;
42
package org.openide.filesystems;
43
43
44
import java.beans.PropertyVetoException;
44
import java.util.ArrayList;
45
import java.util.ArrayList;
45
import java.util.Collections;
46
import java.util.Collections;
46
import java.util.Iterator;
47
import java.util.Iterator;
47
import java.util.List;
48
import java.util.List;
48
import org.netbeans.junit.NbTestCase;
49
import org.netbeans.junit.NbTestCase;
50
import org.openide.filesystems.test.TestFileUtils;
49
51
50
// XXX should only *unused* mask files be listed when propagateMasks?
52
// XXX should only *unused* mask files be listed when propagateMasks?
51
// XXX write similar test for ParsingLayerCacheManager (simulate propagateMasks)
53
// XXX write similar test for ParsingLayerCacheManager (simulate propagateMasks)
Lines 249-254 Link Here
249
    
251
    
250
    // XXX test create -> mask -> recreate in same MFS
252
    // XXX test create -> mask -> recreate in same MFS
251
    
253
    
254
    @SuppressWarnings("deprecation") // for debugging only
255
    private static void setSystemName(FileSystem fs, String s) throws PropertyVetoException {
256
        fs.setSystemName(s);
257
    }
258
    public void testWeightedOverrides() throws Exception { // #141925
259
        FileSystem wr = FileUtil.createMemoryFileSystem();
260
        setSystemName(wr, "wr");
261
        FileSystem fs1 = FileUtil.createMemoryFileSystem();
262
        setSystemName(fs1, "fs1");
263
        FileObject f = TestFileUtils.writeFile(fs1.getRoot(), "d/f", "1");
264
        f.setAttribute("a", 1);
265
        FileSystem fs2 = FileUtil.createMemoryFileSystem();
266
        setSystemName(fs2, "fs2");
267
        f = TestFileUtils.writeFile(fs2.getRoot(), "d/f", "2");
268
        f.setAttribute("a", 2);
269
        // Test behavior with no weights: first layer wins.
270
        FileSystem mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2});
271
        f = mfs.findResource("d/f");
272
        assertEquals(1, f.getAttribute("a"));
273
        assertEquals("1", f.asText());
274
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1});
275
        f = mfs.findResource("d/f");
276
        assertEquals(2, f.getAttribute("a"));
277
        assertEquals("2", f.asText());
278
        // Now test that weighted layer wins over unweighted regardless of order.
279
        fs2.findResource("d/f").setAttribute("weight", 100);
280
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2});
281
        f = mfs.findResource("d/f");
282
        assertEquals(2, f.getAttribute("a"));
283
        assertEquals("2", f.asText());
284
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1});
285
        f = mfs.findResource("d/f");
286
        assertEquals(2, f.getAttribute("a"));
287
        assertEquals("2", f.asText());
288
        // And that a higher weight beats a lower weight.
289
        fs1.findResource("d/f").setAttribute("weight", 200);
290
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2});
291
        f = mfs.findResource("d/f");
292
        assertEquals(1, f.getAttribute("a"));
293
        assertEquals("1", f.asText());
294
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1});
295
        f = mfs.findResource("d/f");
296
        assertEquals(1, f.getAttribute("a"));
297
        assertEquals("1", f.asText());
298
        // Now test writable layer which should always win regardless of weights.
299
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2});
300
        f = mfs.findResource("d/f");
301
        f.setAttribute("a", 0);
302
        TestFileUtils.writeFile(mfs.getRoot(), "d/f", "0");
303
        f = wr.findResource("d/f");
304
        // Oddly, it is null: assertEquals(0, f.getAttribute("a"));
305
        assertEquals("0", f.asText());
306
        mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2});
307
        f = mfs.findResource("d/f");
308
        assertEquals(0, f.getAttribute("a"));
309
        assertEquals("0", f.asText());
310
    }
311
252
    public void testMultipleMaskLayers() throws Exception {
312
    public void testMultipleMaskLayers() throws Exception {
253
        MultiFileSystem fs = new MultiFileSystem(new FileSystem[] {
313
        MultiFileSystem fs = new MultiFileSystem(new FileSystem[] {
254
            TestUtilHid.createXMLFileSystem(getName() + "3", new String[] {
314
            TestUtilHid.createXMLFileSystem(getName() + "3", new String[] {
(-)a/projectui/src/org/netbeans/modules/project/ui/resources/layer.xml (-2 / +4 lines)
Lines 225-233 Link Here
225
        </folder>
225
        </folder>
226
        
226
        
227
        <folder name="Privileged">
227
        <folder name="Privileged">
228
            <!-- overwrite definition of privileged Folder from core/ui -->
229
	    <file name="Folder.shadow">
228
	    <file name="Folder.shadow">
230
                <attr name="originalFile" stringvalue="Templates/Other/Folder"/>
229
                <attr name="originalFile" stringvalue="Templates/Other/Folder"/>
230
                <attr name="position" intvalue="2000"/>
231
            </file>
231
            </file>
232
	</folder>
232
	</folder>
233
233
Lines 246-252 Link Here
246
    <folder name="Windows2">
246
    <folder name="Windows2">
247
        
247
        
248
        <folder name="Modes">
248
        <folder name="Modes">
249
            <file name="explorer.wsmode" url="explorer-override.wsmode"/>
249
            <file name="explorer.wsmode" url="explorer-override.wsmode">
250
                <attr name="weight" intvalue="100"/><!-- override core.ui -->
251
            </file>
250
            <folder name="explorer">            
252
            <folder name="explorer">            
251
                <file name="projectTabLogical_tc.wstcref" url="projectsLogical.wstcref">
253
                <file name="projectTabLogical_tc.wstcref" url="projectsLogical.wstcref">
252
                    <attr name="position" intvalue="100"/>
254
                    <attr name="position" intvalue="100"/>
(-)a/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/layer.xml (-6 lines)
Lines 15-26 Link Here
15
                </file>
15
                </file>
16
            </folder>
16
            </folder>
17
            <folder name="xhtml">
17
            <folder name="xhtml">
18
		<folder name="Popup">
19
		    <file name="org-netbeans-modules-refactoring-api-ui-RSMEditorAction.instance">
20
			<attr name="instanceCreate" methodvalue="org.netbeans.modules.refactoring.api.ui.RefactoringActionsFactory.editorSubmenuAction"/>
21
			<attr name="position" intvalue="1350"/>
22
		    </file>
23
		</folder>
24
		<folder name="RefactoringActions">
18
		<folder name="RefactoringActions">
25
		    <file name="org-netbeans-modules-web-jsf-editor-refactoring-actions-ConvertToCCAction.shadow">
19
		    <file name="org-netbeans-modules-web-jsf-editor-refactoring-actions-ConvertToCCAction.shadow">
26
			<attr name="originalFile" stringvalue="Actions/Refactoring/org-netbeans-modules-web-jsf-editor-refactoring-actions-ConvertToCCAction.instance"/>
20
			<attr name="originalFile" stringvalue="Actions/Refactoring/org-netbeans-modules-web-jsf-editor-refactoring-actions-ConvertToCCAction.instance"/>
(-)a/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/layer.xml (-5 lines)
Lines 20-30 Link Here
20
                <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
20
                <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
21
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/java/resources/class.gif"/>
21
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/java/resources/class.gif"/>
22
            </file>
22
            </file>
23
            <file name="SessionKeyAuthenticator.java" url="/org/netbeans/modules/websvc/saas/codegen/j2ee/resources/SessionKeyAuthenticator.template">
24
                <attr name="position" intvalue="330"/>
25
                <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
26
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/java/resources/class.gif"/>
27
            </file>
28
            <file name="SessionKeyCallback.java" url="/org/netbeans/modules/websvc/saas/codegen/j2ee/resources/SessionKeyCallback.template">
23
            <file name="SessionKeyCallback.java" url="/org/netbeans/modules/websvc/saas/codegen/j2ee/resources/SessionKeyCallback.template">
29
                <attr name="position" intvalue="340"/>
24
                <attr name="position" intvalue="340"/>
30
                <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
25
                <attr name="javax.script.ScriptEngine" stringvalue="freemarker"/>
(-)a/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/resources/SessionKeyAuthenticator.template (-85 lines)
Lines 1-85 Link Here
1
<#-- FreeMarker template (see http://freemarker.org/) -->
2
<#assign licenseFirst = "/*">
3
<#assign licensePrefix = " * ">
4
<#assign licenseLast = " */">
5
<#include "../Licenses/license-${project.license}.txt"> 
6
7
<#if package?? && package != "">
8
package ${package};
9
10
</#if>
11
import java.io.IOException;
12
import java.math.BigInteger;
13
import java.net.URLEncoder;
14
import java.security.MessageDigest;
15
import java.util.Map;
16
import java.util.Properties;
17
import java.util.Set;
18
import java.util.TreeMap;
19
import java.util.logging.Level;
20
import java.util.logging.Logger;
21
import org.netbeans.saas.RestConnection;
22
23
/**
24
 *
25
 * @author ${user}
26
 */
27
public class ${name} {
28
29
    private static String apiKey;
30
    private static String secret;
31
    
32
    static {
33
        try {
34
            Properties props = new Properties();
35
            props.load(${name}.class.getResourceAsStream(
36
                    "profile.properties"));
37
            apiKey = props.getProperty("api_key");
38
            secret = props.getProperty("secret");
39
        } catch (IOException ex) {
40
            Logger.getLogger(${name}.class.getName()).log(Level.SEVERE, null, ex);
41
        }
42
    }
43
44
    public static String getApiKey() {
45
        return apiKey;
46
    }
47
48
    public static String sign(String[][] params) {
49
        return sign(secret, params);
50
    }
51
52
    private static String sign(String secret,
53
            String[][] params) {
54
55
        try {
56
            TreeMap<String, String> map = new TreeMap<String, String>();
57
58
            for (int i = 0; i < params.length; i++) {
59
                String key = params[i][0];
60
                String value = params[i][1];
61
62
                if (value != null) {
63
                    map.put(key, URLEncoder.encode(value, "UTF-8"));
64
                }
65
            }
66
67
            String signature = "";
68
            Set<Map.Entry<String, String>> entrySet = map.entrySet();
69
            for (Map.Entry<String, String> entry : entrySet) {
70
                signature += entry.getKey() + "=" + entry.getValue();
71
            }
72
            signature += secret;
73
74
            MessageDigest md = MessageDigest.getInstance("MD5");
75
            byte[] sum = md.digest(signature.getBytes("UTF-8"));
76
            BigInteger bigInt = new BigInteger(1, sum);
77
78
            return bigInt.toString(16);
79
        } catch (Exception ex) {
80
            Logger.getLogger(${name}.class.getName()).log(Level.SEVERE, null, ex);
81
        }
82
83
        return null;
84
    }
85
}

Return to bug 141925