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 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 servicesList; + protected HashMap register; + + public ServicesHolder(Class clazz) { + this.clazz = clazz; + } + + private final void init() { + register = new HashMap(); + final List 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 {