diff --git a/api.debugger/apichanges.xml b/api.debugger/apichanges.xml
--- a/api.debugger/apichanges.xml
+++ b/api.debugger/apichanges.xml
@@ -309,6 +309,25 @@
+
+
+ Support for providing initial values of properties.
+
+
+
+
+
+
+ Properties.Initializer interface introduced to provide initial
+ values of properties. This is necessary when properties are
+ accessed from more places and it's not practical to copy default
+ values to every such location.
+
+
+
+
+
+
diff --git a/api.debugger/manifest.mf b/api.debugger/manifest.mf
--- a/api.debugger/manifest.mf
+++ b/api.debugger/manifest.mf
@@ -1,4 +1,4 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.api.debugger/1
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/debugger/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.16
+OpenIDE-Module-Specification-Version: 1.17
diff --git a/api.debugger/src/org/netbeans/api/debugger/Properties.java b/api.debugger/src/org/netbeans/api/debugger/Properties.java
--- a/api.debugger/src/org/netbeans/api/debugger/Properties.java
+++ b/api.debugger/src/org/netbeans/api/debugger/Properties.java
@@ -66,6 +66,7 @@
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
@@ -355,7 +356,28 @@
*/
public void write (Object object, Properties properties);
}
-
+
+ /**
+ * Implementing this interface one can define initial values of properties.
+ */
+ public interface Initializer {
+
+ /**
+ * The list of supported property names.
+ * @return the property names supported by this initializer
+ */
+ String[] getSupportedPropertyNames();
+
+ /**
+ * Get the default value of property.
+ * @param propertyName The name of the property
+ * @return The default value
+ */
+ Object getDefaultPropertyValue(String propertyName);
+
+ }
+
+
private final static class PrimitiveRegister {
private HashMap properties = new HashMap ();
@@ -486,100 +508,172 @@
private static final Map BAD_MAP = new HashMap ();
private static final Collection BAD_COLLECTION = new ArrayList ();
private static final Object[] BAD_ARRAY = new Object [0];
-
- private List extends Reader> readersList;
- private HashMap register;
-
-
+
+ ServicesHolder readers = new ReaderHolder();
+ ServicesHolder initializers = new InitializerHolder();
+
+ private static abstract class ServicesHolder {
+
+ private Class clazz;
+ // Holds the list to prevent from garbage-collect. Do not remove!
+ private List extends T> servicesList;
+ protected HashMap register;
+
+ public ServicesHolder(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ private final void init() {
+ register = new HashMap();
+ final List extends T> list = DebuggerManager.getDebuggerManager().lookup(null, clazz);
+ servicesList = list;
+ synchronized (list) {
+ for (T s : list) {
+ registerService(s);
+ }
+ }
+ ((Customizer) list).addPropertyChangeListener(
+ new PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent evt) {
+ synchronized (ServicesHolder.this) {
+ Set registeredServices = new HashSet(register.values());
+ synchronized (list) {
+ for (T s : list) {
+ if (!registeredServices.remove(s)) {
+ registerService(s);
+ }
+ }
+ }
+ for (T s : registeredServices) {
+ unregisterService(s);
+ }
+ }
+ }
+ });
+ ((Customizer) list).setObject(Lookup.NOTIFY_LOAD_FIRST);
+ ((Customizer) list).setObject(Lookup.NOTIFY_UNLOAD_LAST);
+ }
+
+ protected abstract void registerService(T s);
+
+ protected abstract void unregisterService(T s);
+
+ public synchronized T find(String name) {
+ if (register == null) {
+ init();
+ }
+ return register.get(name);
+ }
+
+ }
+
+ private static final class ReaderHolder extends ServicesHolder {
+
+ public ReaderHolder() {
+ super(Reader.class);
+ }
+
+ @Override
+ protected void registerService(Reader r) {
+ //System.err.println("registerReader("+r+")");
+ String[] ns = r.getSupportedClassNames ();
+ int j, jj = ns.length;
+ for (j = 0; j < jj; j++) {
+ register.put (ns [j], r);
+ }
+ }
+
+ @Override
+ protected void unregisterService(Reader r) {
+ //System.err.println("unregisterReader("+r+")");
+ String[] ns = r.getSupportedClassNames ();
+ int j, jj = ns.length;
+ for (j = 0; j < jj; j++) {
+ register.remove (ns [j]);
+ }
+ }
+
+ @Override
+ public synchronized Reader find(String typeID) {
+
+ Reader r = super.find(typeID);
+ if (r != null) return r;
+
+ Class c = null;
+ try {
+ c = getClassLoader ().loadClass (typeID);
+ } catch (ClassNotFoundException e) {
+ ErrorManager.getDefault().notify(e);
+ return null;
+ }
+ while ((c != null) && (register.get (c.getName ()) == null)) {
+ c = c.getSuperclass ();
+ }
+ if (c != null)
+ r = (Reader) register.get (c.getName ());
+ return r;
+ }
+
+ }
+
+ private static final class InitializerHolder extends ServicesHolder {
+
+ public InitializerHolder() {
+ super(Initializer.class);
+ }
+
+ @Override
+ protected void registerService(Initializer i) {
+ //System.err.println("registerInitializer("+i+")");
+ String[] ns = i.getSupportedPropertyNames();
+ int j, jj = ns.length;
+ for (j = 0; j < jj; j++) {
+ register.put (ns [j], i);
+ }
+ }
+
+ @Override
+ protected void unregisterService(Initializer i) {
+ //System.err.println("unregisterInitializer("+i+")");
+ String[] ns = i.getSupportedPropertyNames ();
+ int j, jj = ns.length;
+ for (j = 0; j < jj; j++) {
+ register.remove (ns [j]);
+ }
+ }
+ }
+
+
private PrimitiveRegister impl = new PrimitiveRegister ();
- private void initReaders () {
- register = new HashMap();
- readersList = DebuggerManager.getDebuggerManager().lookup(null, Reader.class);
- synchronized (readersList) {
- for (Reader r : readersList) {
- registerReader(r);
+
+ private T getInitialValue(String propertyName, Class clazz) {
+ Initializer initializer = initializers.find(propertyName);
+ if (initializer != null) {
+ Object value = initializer.getDefaultPropertyValue(propertyName);
+ if (value != null && !clazz.isAssignableFrom(value.getClass())) {
+ Exceptions.printStackTrace(new IllegalStateException(
+ "Value ("+value+") of a bad type ("+value.getClass()+") returned by "+initializer+
+ " for property '"+propertyName+"'. It can not be cast to "+clazz));
+ value = null;
}
- }
- ((Customizer) readersList).addPropertyChangeListener(
- new PropertyChangeListener() {
- public void propertyChange(PropertyChangeEvent evt) {
- synchronized (PropertiesImpl.this) {
- Set registeredReaders = new HashSet(register.values());
- synchronized (readersList) {
- for (Reader r : readersList) {
- if (!registeredReaders.remove(r)) {
- registerReader(r);
- }
- }
- }
- for (Reader r : registeredReaders) {
- unregisterReader(r);
- }
- }
- }
- });
- ((Customizer) readersList).setObject(Lookup.NOTIFY_LOAD_FIRST);
- ((Customizer) readersList).setObject(Lookup.NOTIFY_UNLOAD_LAST);
- }
-
- private void registerReader(Reader r) {
- //System.err.println("registerReader("+r+")");
- String[] ns = r.getSupportedClassNames ();
- int j, jj = ns.length;
- for (j = 0; j < jj; j++) {
- register.put (ns [j], r);
+ return (T) value;
+ } else {
+ return null;
}
}
-
- private void unregisterReader(Reader r) {
- //System.err.println("unregisterReader("+r+")");
- String[] ns = r.getSupportedClassNames ();
- int j, jj = ns.length;
- for (j = 0; j < jj; j++) {
- register.remove (ns [j]);
- }
- }
-
- // Used from tests
- synchronized void addReader(Reader r) {
- if (register == null) {
- initReaders ();
- }
- registerReader(r);
- }
-
- private synchronized Reader findReader (String typeID) {
- if (register == null) {
- initReaders ();
- }
-
- Reader r = (Reader) register.get (typeID);
- if (r != null) return r;
-
- Class c = null;
- try {
- c = getClassLoader ().loadClass (typeID);
- } catch (ClassNotFoundException e) {
- ErrorManager.getDefault().notify(e);
- return null;
- }
- while ((c != null) && (register.get (c.getName ()) == null)) {
- c = c.getSuperclass ();
- }
- if (c != null)
- r = (Reader) register.get (c.getName ());
- return r;
- }
-
-
-
// primitive properties ....................................................................................
public String getString (String propertyName, String defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ String initialValue = getInitialValue(propertyName, String.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
if (!value.startsWith ("\"")) {
ErrorManager.getDefault().log("Can not read string " + value + ".");
return defaultValue;
@@ -597,7 +691,13 @@
public int getInt (String propertyName, int defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Integer initialValue = getInitialValue(propertyName, Integer.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
try {
int val = Integer.parseInt (value);
return val;
@@ -612,7 +712,13 @@
public char getChar (String propertyName, char defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Character initialValue = getInitialValue(propertyName, Character.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
char val = value.charAt (0);
return val;
}
@@ -623,7 +729,13 @@
public float getFloat (String propertyName, float defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Float initialValue = getInitialValue(propertyName, Float.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
try {
float val = Float.parseFloat (value);
return val;
@@ -638,7 +750,13 @@
public long getLong (String propertyName, long defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Long initialValue = getInitialValue(propertyName, Long.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
try {
long val = Long.parseLong (value);
return val;
@@ -653,7 +771,13 @@
public double getDouble (String propertyName, double defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Double initialValue = getInitialValue(propertyName, Double.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
try {
double val = Double.parseDouble (value);
return val;
@@ -668,7 +792,13 @@
public boolean getBoolean (String propertyName, boolean defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Boolean initialValue = getInitialValue(propertyName, Boolean.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
boolean val = value.equals ("true");
return val;
}
@@ -679,7 +809,13 @@
public byte getByte (String propertyName, byte defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Byte initialValue = getInitialValue(propertyName, Byte.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
try {
byte val = Byte.parseByte (value);
return val;
@@ -694,7 +830,13 @@
public short getShort (String propertyName, short defaultValue) {
String value = impl.getProperty (propertyName, null);
- if (value == null) return defaultValue;
+ if (value == null) {
+ Short initialValue = getInitialValue(propertyName, Short.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
try {
short val = Short.parseShort (value);
return val;
@@ -710,7 +852,13 @@
public Object getObject (String propertyName, Object defaultValue) {
synchronized(impl) {
String typeID = impl.getProperty (propertyName, null);
- if (typeID == null) return defaultValue;
+ if (typeID == null) {
+ Object initialValue = getInitialValue(propertyName, Object.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
if (typeID.equals ("# null"))
return null;
if (!typeID.startsWith ("# ")) {
@@ -745,7 +893,7 @@
return co;
}
}
- Reader r = findReader (typeID);
+ Reader r = readers.find(typeID);
if (r == null) {
ErrorManager.getDefault().log("Can not read object. No reader registered for type " + typeID + ".");
return defaultValue;
@@ -778,7 +926,7 @@
}
// find register
- Reader r = findReader (value.getClass ().getName ());
+ Reader r = readers.find(value.getClass ().getName ());
if (r == null) {
ErrorManager.getDefault().log ("Can not write object " + value);
return;
@@ -794,8 +942,11 @@
synchronized(impl) {
String arrayType = impl.getProperty (propertyName + ".array_type", null);
if (arrayType == null) {
- ErrorManager.getDefault().log("Unknown array type for "+propertyName);
- return defaultValue;
+ Object[] initialValue = getInitialValue(propertyName, Object[].class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
}
Properties p = getProperties (propertyName);
int l = p.getInt ("length", -1);
@@ -834,7 +985,13 @@
public Collection getCollection (String propertyName, Collection defaultValue) {
synchronized(impl) {
String typeID = impl.getProperty (propertyName, null);
- if (typeID == null) return defaultValue;
+ if (typeID == null) {
+ Collection initialValue = getInitialValue(propertyName, Collection.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
if (!typeID.startsWith ("# ")) return defaultValue;
Collection c = null;
try {
@@ -886,7 +1043,13 @@
public Map getMap (String propertyName, Map defaultValue) {
synchronized(impl) {
String typeID = impl.getProperty (propertyName, null);
- if (typeID == null) return defaultValue;
+ if (typeID == null) {
+ Map initialValue = getInitialValue(propertyName, Map.class);
+ if (initialValue == null) {
+ initialValue = defaultValue;
+ }
+ return initialValue;
+ }
if (!typeID.startsWith ("# ")) return defaultValue;
Map m = null;
try {
diff --git a/spi.debugger.ui/test/unit/src/org/netbeans/api/debugger/PropertiesTest.java b/spi.debugger.ui/test/unit/src/org/netbeans/api/debugger/PropertiesTest.java
--- a/spi.debugger.ui/test/unit/src/org/netbeans/api/debugger/PropertiesTest.java
+++ b/spi.debugger.ui/test/unit/src/org/netbeans/api/debugger/PropertiesTest.java
@@ -47,8 +47,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import junit.framework.TestCase;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
/**
* Test of the Properties class
@@ -109,7 +111,6 @@
public void testReader() throws Exception {
Properties p = Properties.getDefault();
- ((Properties.PropertiesImpl) p).addReader(new TestReader());
assertNull(p.getObject("rect 1", null));
assertNull(p.getObject("rect 2", null));
assertNull(p.getObject("test 1", null));
@@ -132,11 +133,52 @@
assertEquals(t2, p.getObject("test 2", null));
assertEquals(t3, p.getObject("test 3", null));
}
+
+ public void testInitializer() throws Exception {
+ Properties p = Properties.getDefault();
+ p = p.getProperties("test");
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.array"), p.getArray("array", null));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.boolean"), p.getBoolean("boolean", true));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.boolean"), p.getBoolean("boolean", false));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.byte"), p.getByte("byte", (byte) 0));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.byte"), p.getByte("byte", (byte) -1));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.char"), p.getChar("char", (char) 0));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.char"), p.getChar("char", 'a'));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.collection"), p.getCollection("collection", null));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.set"), p.getCollection("set", null));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.list"), p.getCollection("list", null));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.double"), p.getDouble("double", 0.0));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.double"), p.getDouble("double", 1.0));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.float"), p.getFloat("float", 0.0f));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.float"), p.getFloat("float", 1.0f));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.int"), p.getInt("int", 0));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.int"), p.getInt("int", 1));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.long"), p.getLong("long", 0l));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.long"), p.getLong("long", 1234567890123456789l));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.map"), p.getMap("map", null));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.linkedhashmap"), p.getMap("linkedhashmap", null));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.object"), p.getObject("object", null));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.short"), p.getShort("short", (short) 0));
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.short"), p.getShort("short", (short) 1));
+
+ assertEquals(TestInitializer.DEFAULT_VALUES.get("test.string"), p.getString("string", null));
+ }
/** Stress test of multi-threaded get/set */
public void testStressGetSet() throws Exception {
Properties p = Properties.getDefault();
- ((Properties.PropertiesImpl) p).addReader(new TestReader());
int n = 5;
ConcurrentGetSet[] cgs = new ConcurrentGetSet[n];
Thread[] t = new Thread[n];
@@ -308,8 +350,9 @@
p.setObject(name, obj);
assertEquals(obj, p.getObject(name, ""));
}
-
- private static class TestReader implements Properties.Reader {
+
+ @DebuggerServiceRegistration(types={Properties.Reader.class})
+ public static class TestReader implements Properties.Reader {
public String[] getSupportedClassNames() {
@@ -340,6 +383,59 @@
}
throw new IllegalArgumentException(object.toString());
}
+ }
+
+ @DebuggerServiceRegistration(types={Properties.Initializer.class})
+ public static class TestInitializer implements Properties.Initializer {
+
+ public static final String[] PROPERTY_NAMES = new String[] {
+ "test.array",
+ "test.boolean",
+ "test.byte",
+ "test.char",
+ "test.collection",
+ "test.set",
+ "test.list",
+ "test.double",
+ "test.float",
+ "test.int",
+ "test.long",
+ "test.map",
+ "test.linkedhashmap",
+ "test.object",
+ "test.short",
+ "test.string",
+ };
+
+ public static final Map DEFAULT_VALUES = new HashMap();
+
+ static {
+ DEFAULT_VALUES.put(PROPERTY_NAMES[0], new String[] {"TestArray"});
+ DEFAULT_VALUES.put(PROPERTY_NAMES[1], Boolean.TRUE);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[2], Byte.MAX_VALUE);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[3], Character.MIN_SURROGATE);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[4], Collections.unmodifiableCollection(Collections.singleton(DEFAULT_VALUES)));
+ DEFAULT_VALUES.put(PROPERTY_NAMES[5], Collections.singleton(PROPERTY_NAMES));
+ DEFAULT_VALUES.put(PROPERTY_NAMES[6], Collections.singletonList("sl"));
+ DEFAULT_VALUES.put(PROPERTY_NAMES[7], Double.NaN);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[8], Float.NEGATIVE_INFINITY);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[9], Integer.SIZE);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[10], Long.reverse(Long.SIZE));
+ DEFAULT_VALUES.put(PROPERTY_NAMES[11], Collections.singletonMap("key", "value"));
+ DEFAULT_VALUES.put(PROPERTY_NAMES[12], new LinkedHashMap());
+ DEFAULT_VALUES.put(PROPERTY_NAMES[13], new Rectangle(10, 10, 20, 20));
+ DEFAULT_VALUES.put(PROPERTY_NAMES[14], Short.MIN_VALUE);
+ DEFAULT_VALUES.put(PROPERTY_NAMES[15], String.class.getName());
+ }
+
+ public String[] getSupportedPropertyNames() {
+ return PROPERTY_NAMES;
+ }
+
+ public Object getDefaultPropertyValue(String propertyName) {
+ return DEFAULT_VALUES.get(propertyName);
+ }
+
}
private static class TestObject {