(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) {
@@ -184,8 +189,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;
+ }
}
}
}
@@ -297,6 +306,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;
@@ -304,11 +317,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()).log(
+ Level.WARNING, "File {0} in {1} has nonnumeric weight {2} of type {3}",
+ new Object[] {f.getPath(), f.getFileSystem(), weight, weight.getClass().getName()});
+ } catch (FileStateInvalidException x) {/* ignore */}
+ return 0;
+ }
}
/** Getter for the right file system */
@@ -774,6 +812,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) {
@@ -793,7 +835,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;
+ }
}
}
@@ -807,12 +853,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 static boolean sameFullName(FileObject f1, FileObject f2) {
--- a/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java
+++ a/openide.filesystems/src/org/openide/filesystems/MultiFileSystem.java
@@ -61,10 +61,19 @@
*
* This filesystem has no form of storage in and of itself. Rather, it composes and proxies one or more
* "delegate" filesystems. The result is that of a "layered" sandwich of filesystems, each able to provide files
- * to appear in the merged result. The layers are ordered so that a filesystem in "front" can override one in
- * "back". Often the frontmost layer will be writable, and all changes to the filesystem are sent to this layer,
+ * to appear in the merged result.
+ * Often the frontmost layer will be writable, and all changes to the filesystem are sent to this layer,
* but that behavior is configurable.
*
+ *
The layers are ordered so that entries in a filesystem in "front" can override one in "back".
+ * Since it is often not straightforward to arrange layers so that particular overrides work,
+ * as of org.openide.filesystems 7.36
+ * you may set the special file attribute {@code weight} to any {@link Number} on a layer entry.
+ * (If unspecified, the implicit default value is zero.)
+ * A variant with a higher weight will override one with a lower weight even if it is further back.
+ * (The exception is that entries in a writable frontmost layer always override other layers,
+ * regardless of weight.)
+ *
*
Creating a new MultiFileSystem
is easy in the simplest cases: just call {@link
* #MultiFileSystem(FileSystem[])} and pass a list of delegates. If you pass it only read-only delegates, the
* composite will also be read-only. Or you may pass it one or more writable filesystems (make sure the first
@@ -448,6 +457,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)
@@ -555,6 +573,7 @@
Enumeration delegates(final String name) {
Enumeration en = Enumerations.array(systems);
+ // XXX order (stably) by weight
class Resources implements Enumerations.Processor {
public @Override FileObject process(FileSystem fs, Collection ignore) {
if (fs == null) {
--- a/openide.filesystems/test/unit/src/org/openide/filesystems/MultiFileSystemMaskTest.java
+++ a/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,6 +251,64 @@
// 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", f.asText());
+ mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1});
+ f = mfs.findResource("d/f");
+ assertEquals(2, f.getAttribute("a"));
+ assertEquals("2", f.asText());
+ // 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", f.asText());
+ mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1});
+ f = mfs.findResource("d/f");
+ assertEquals(2, f.getAttribute("a"));
+ assertEquals("2", f.asText());
+ // 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", f.asText());
+ mfs = new MultiFileSystem(new FileSystem[] {wr, fs2, fs1});
+ f = mfs.findResource("d/f");
+ assertEquals(1, f.getAttribute("a"));
+ assertEquals("1", f.asText());
+ // 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", f.asText());
+ mfs = new MultiFileSystem(new FileSystem[] {wr, fs1, fs2});
+ f = mfs.findResource("d/f");
+ assertEquals(0, f.getAttribute("a"));
+ assertEquals("0", f.asText());
+ }
+
public void testMultipleMaskLayers() throws Exception {
MultiFileSystem fs = new MultiFileSystem(new FileSystem[] {
TestUtilHid.createXMLFileSystem(getName() + "3", new String[] {
--- a/projectui/src/org/netbeans/modules/project/ui/resources/layer.xml
+++ a/projectui/src/org/netbeans/modules/project/ui/resources/layer.xml
@@ -225,9 +225,9 @@
-
+
@@ -246,7 +246,9 @@
-
+
+
+
--- a/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/layer.xml
+++ a/web.jsf.editor/src/org/netbeans/modules/web/jsf/editor/resources/layer.xml
@@ -15,12 +15,6 @@
-
-
-
-
-
-
--- a/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/layer.xml
+++ a/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/layer.xml
@@ -20,11 +20,6 @@
-
-
-
-
-
--- a/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/resources/SessionKeyAuthenticator.template
+++ a/websvc.saas.codegen.j2ee/src/org/netbeans/modules/websvc/saas/codegen/j2ee/resources/SessionKeyAuthenticator.template
@@ -1,85 +0,0 @@
-<#-- FreeMarker template (see http://freemarker.org/) -->
-<#assign licenseFirst = "/*">
-<#assign licensePrefix = " * ">
-<#assign licenseLast = " */">
-<#include "../Licenses/license-${project.license}.txt">
-
-<#if package?? && package != "">
-package ${package};
-
-#if>
-import java.io.IOException;
-import java.math.BigInteger;
-import java.net.URLEncoder;
-import java.security.MessageDigest;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.netbeans.saas.RestConnection;
-
-/**
- *
- * @author ${user}
- */
-public class ${name} {
-
- private static String apiKey;
- private static String secret;
-
- static {
- try {
- Properties props = new Properties();
- props.load(${name}.class.getResourceAsStream(
- "profile.properties"));
- apiKey = props.getProperty("api_key");
- secret = props.getProperty("secret");
- } catch (IOException ex) {
- Logger.getLogger(${name}.class.getName()).log(Level.SEVERE, null, ex);
- }
- }
-
- public static String getApiKey() {
- return apiKey;
- }
-
- public static String sign(String[][] params) {
- return sign(secret, params);
- }
-
- private static String sign(String secret,
- String[][] params) {
-
- try {
- TreeMap map = new TreeMap();
-
- for (int i = 0; i < params.length; i++) {
- String key = params[i][0];
- String value = params[i][1];
-
- if (value != null) {
- map.put(key, URLEncoder.encode(value, "UTF-8"));
- }
- }
-
- String signature = "";
- Set> entrySet = map.entrySet();
- for (Map.Entry entry : entrySet) {
- signature += entry.getKey() + "=" + entry.getValue();
- }
- signature += secret;
-
- MessageDigest md = MessageDigest.getInstance("MD5");
- byte[] sum = md.digest(signature.getBytes("UTF-8"));
- BigInteger bigInt = new BigInteger(1, sum);
-
- return bigInt.toString(16);
- } catch (Exception ex) {
- Logger.getLogger(${name}.class.getName()).log(Level.SEVERE, null, ex);
- }
-
- return null;
- }
-}