Issue #141925: provide a simpler way to declare which layer entries override others. diff --git a/openide.filesystems/src/org/openide/filesystems/MultiFileObject.java b/openide.filesystems/src/org/openide/filesystems/MultiFileObject.java --- a/openide.filesystems/src/org/openide/filesystems/MultiFileObject.java +++ b/openide.filesystems/src/org/openide/filesystems/MultiFileObject.java @@ -60,6 +60,7 @@ import java.util.Properties; import java.util.Set; import java.util.WeakHashMap; +import java.util.logging.Logger; /** Implementation of the file object for multi file system. * @@ -74,6 +75,7 @@ /** default path separator */ private static final char PATH_SEP = '/'; + private static final String WEIGHT_ATTRIBUTE = "weight"; // NOI18N private static final FileSystem.AtomicAction markAtomicAction = new FileSystem.AtomicAction() { public void run() { } @@ -164,9 +166,11 @@ Set now = (delegates == null) ? Collections.EMPTY_SET : delegates; Set del = new HashSet(arr.length * 2); + Number maxWeight = 0; FileObject led = null; String name = getPath(); + FileSystem writable = mfs.writableLayer(name); for (int i = 0; i < arr.length; i++) { if (arr[i] != null) { @@ -180,8 +184,12 @@ fo.addFileChangeListener(weakL); } - if ((led == null) && fo.isValid()) { - led = fo; + if (fo.isValid()) { + Number weight = weightOf(fo, writable); + if (led == null || weight.doubleValue() > maxWeight.doubleValue()) { + led = fo; + maxWeight = weight; + } } } } @@ -293,6 +301,10 @@ private FileObject findLeader(FileSystem[] fs, String path) { MultiFileSystem mfs = getMultiFileSystem(); + Number maxWeight = 0; + FileObject _leader = null; + FileSystem writable = mfs.writableLayer(path); + for (FileSystem f : fs) { if (f == null) { continue; @@ -300,11 +312,36 @@ FileObject fo = mfs.findResourceOn(f, path); if (fo != null) { - return fo; + Number weight = weightOf(fo, writable); + if (_leader == null || weight.doubleValue() > maxWeight.doubleValue()) { + _leader = fo; + maxWeight = weight; + } } } - return null; + return _leader; + } + + private static Number weightOf(FileObject f, FileSystem writable) { + try { + if (f.getFileSystem() == writable) { + return Double.MAX_VALUE; + } + } catch (FileStateInvalidException x) {/* ignore */} + Object weight = f.getAttribute(WEIGHT_ATTRIBUTE); + if (weight instanceof Number) { + return (Number) weight; + } else if (weight == null) { + return 0; + } else { + try { + Logger.getLogger(MultiFileObject.class.getName()).warning( + "File " + f.getPath() + " in " + f.getFileSystem() + + " has nonnumeric weight " + weight + " of type " + weight.getClass().getName()); + } catch (FileStateInvalidException x) {/* ignore */} + return 0; + } } /** Getter for the right file system */ @@ -767,6 +804,10 @@ FileSystem[] systems = getMultiFileSystem().getDelegates(); + Number maxWeight = 0; + Object attr = null; + FileSystem writable = getMultiFileSystem().writableLayer(path); + // boolean isLoaderAttr = /* DataObject.EA_ASSIGNED_LOADER */ "NetBeansAttrAssignedLoader".equals (attrName); // NOI18N for (int i = 0; i < systems.length; i++) { if (systems[i] == null) { @@ -786,7 +827,11 @@ Object o = getAttribute(fo, attrName, fo.getPath()); // Performance tricks: if (o != null) { - return devoidify(o); + Number weight = weightOf(fo, writable); + if (attr == null || weight.doubleValue() > maxWeight.doubleValue()) { + attr = o; + maxWeight = weight; + } } } @@ -800,12 +845,16 @@ Object o = getAttribute(fo, prefixattr, ""); // NOI18N if (o != null) { - return devoidify(o); + Number weight = weightOf(fo, writable); + if (attr == null || weight.doubleValue() > maxWeight.doubleValue()) { + attr = o; + maxWeight = weight; + } } } } - return null; + return devoidify(attr); } private Object getAttribute(FileObject fo, String attrName, String path) { diff --git a/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java b/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java --- a/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java +++ b/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java @@ -447,6 +447,15 @@ return systems[WRITE_SYSTEM_INDEX]; } + FileSystem writableLayer(String path) { + try { + return createWritableOn(path); + } catch (IOException x) { + // ignore + return systems.length > WRITE_SYSTEM_INDEX ? systems[WRITE_SYSTEM_INDEX] : null; + } + } + /** Special case of createWritableOn (@see #createWritableOn). * * @param oldName original name of the file (full) @@ -554,6 +563,7 @@ Enumeration delegates(final String name) { Enumeration en = Enumerations.array(systems); + // XXX order (stably) by weight class Resources implements Enumerations.Processor { public FileObject process(FileSystem fs, Collection ignore) { if (fs == null) { diff --git a/openide.filesystems/test/unit/src/org/openide/filesystems/MultiFileSystemMaskTest.java b/openide.filesystems/test/unit/src/org/openide/filesystems/MultiFileSystemMaskTest.java --- a/openide.filesystems/test/unit/src/org/openide/filesystems/MultiFileSystemMaskTest.java +++ b/openide.filesystems/test/unit/src/org/openide/filesystems/MultiFileSystemMaskTest.java @@ -41,11 +41,13 @@ package org.openide.filesystems; +import java.beans.PropertyVetoException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.test.TestFileUtils; // XXX should only *unused* mask files be listed when propagateMasks? // XXX write similar test for ParsingLayerCacheManager (simulate propagateMasks) @@ -249,4 +251,62 @@ // XXX test create -> mask -> recreate in same MFS + @SuppressWarnings("deprecation") // for debugging only + private static void setSystemName(FileSystem fs, String s) throws PropertyVetoException { + fs.setSystemName(s); + } + public void testWeightedOverrides() throws Exception { // #141925 + FileSystem wr = FileUtil.createMemoryFileSystem(); + setSystemName(wr, "wr"); + FileSystem fs1 = FileUtil.createMemoryFileSystem(); + setSystemName(fs1, "fs1"); + FileObject f = TestFileUtils.writeFile(fs1.getRoot(), "d/f", "1"); + f.setAttribute("a", 1); + FileSystem fs2 = FileUtil.createMemoryFileSystem(); + setSystemName(fs2, "fs2"); + f = TestFileUtils.writeFile(fs2.getRoot(), "d/f", "2"); + f.setAttribute("a", 2); + // Test behavior with no weights: first layer wins. + FileSystem mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2}); + f = mfs.findResource("d/f"); + assertEquals(1, f.getAttribute("a")); + assertEquals("1", TestFileUtils.readFile(f)); + mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1}); + f = mfs.findResource("d/f"); + assertEquals(2, f.getAttribute("a")); + assertEquals("2", TestFileUtils.readFile(f)); + // Now test that weighted layer wins over unweighted regardless of order. + fs2.findResource("d/f").setAttribute("weight", 100); + mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2}); + f = mfs.findResource("d/f"); + assertEquals(2, f.getAttribute("a")); + assertEquals("2", TestFileUtils.readFile(f)); + mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1}); + f = mfs.findResource("d/f"); + assertEquals(2, f.getAttribute("a")); + assertEquals("2", TestFileUtils.readFile(f)); + // And that a higher weight beats a lower weight. + fs1.findResource("d/f").setAttribute("weight", 200); + mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2}); + f = mfs.findResource("d/f"); + assertEquals(1, f.getAttribute("a")); + assertEquals("1", TestFileUtils.readFile(f)); + mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1}); + f = mfs.findResource("d/f"); + assertEquals(1, f.getAttribute("a")); + assertEquals("1", TestFileUtils.readFile(f)); + // Now test writable layer which should always win regardless of weights. + mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2}); + f = mfs.findResource("d/f"); + f.setAttribute("a", 0); + TestFileUtils.writeFile(mfs.getRoot(), "d/f", "0"); + f = wr.findResource("d/f"); + // Oddly, it is null: assertEquals(0, f.getAttribute("a")); + assertEquals("0", TestFileUtils.readFile(f)); + mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2}); + f = mfs.findResource("d/f"); + assertEquals(0, f.getAttribute("a")); + assertEquals("0", TestFileUtils.readFile(f)); + } + }