Author: mes
Date: 2012-03-16 14:43:48 -0700 (Fri, 16 Mar 2012)
New Revision: 28569

Added:
   
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/ContainsTunables.java
   
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/JustTunables.java
Modified:
   
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/AbstractTunableInterceptor.java
   
core3/api/trunk/work-api/src/test/java/org/cytoscape/work/AbstractTunableInterceptorTest.java
   
core3/impl/trunk/command-executor-impl/src/main/java/org/cytoscape/command/internal/tunables/CommandTunableInterceptorImpl.java
   
core3/impl/trunk/swing-application-impl/src/main/java/org/cytoscape/internal/commands/ArgRecorder.java
   
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/submenu/SubmenuTunableMutator.java
   
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/sync/SyncTunableMutator.java
   
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/tunables/JPanelTunableMutator.java
   
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/ScootersTunableTask.java
Log:
Added support for ContainsTunable annotation which allows tunables to be 
composed from multiple objects.

Modified: 
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/AbstractTunableInterceptor.java
===================================================================
--- 
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/AbstractTunableInterceptor.java
   2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/AbstractTunableInterceptor.java
   2012-03-16 21:43:48 UTC (rev 28569)
@@ -53,14 +53,9 @@
        /**
         *  Store the Handlers.
         */
-       protected final Map<Object, LinkedHashMap<String, T>> handlerMap;
+       protected final Map<Object, List<T>> handlerMap;
 
        /**
-        *  Store the JPanel-returning methods.
-        */
-       protected final Map<Object, Method> guiProviderMap;
-
-       /**
         *  Store the title-returning methods.
         */
        protected final Map<Object, Method> titleProviderMap;
@@ -77,8 +72,7 @@
         */
        public AbstractTunableInterceptor() {
                throwException = false;
-               handlerMap = new HashMap<Object, LinkedHashMap<String, T>>();
-               guiProviderMap = new HashMap<Object, Method>();
+               handlerMap = new HashMap<Object, List<T>>();
                titleProviderMap = new HashMap<Object, Method>();
                tunableHandlerFactories = new 
ArrayList<TunableHandlerFactory<T>>();
        }
@@ -88,7 +82,6 @@
                this.throwException = throwException;
        }
 
-       // TODO make this private!!!
        /**
         * To detect fields and methods annotated with {@link Tunable}, create 
         * a {@link TunableHandler} for each from the factory, and store it in 
handlerMap.
@@ -96,9 +89,11 @@
         * @param obj A class that contains fields or methods annotated with 
{@link Tunable} 
         * whose value needs to be set or recorded. 
         */
-       protected void loadTunables(final Object obj) {
-               if (!handlerMap.containsKey(obj)) {
-                       final LinkedHashMap<String, T> handlerList = new 
LinkedHashMap<String, T>();
+       private List<T> loadTunables(final Object obj) {
+               List<T> handlerList = handlerMap.get(obj);
+               if (handlerList == null) {
+                       handlerList = new ArrayList<T>();
+                       handlerMap.put(obj, handlerList);
 
                        // Find each public field in the class.
                        for (final Field field : obj.getClass().getFields()) {
@@ -113,18 +108,27 @@
 
                                                // ...add it to the list of 
Handlers
                                                if (handler != null)
-                                                       
handlerList.put(field.getName(), handler);
+                                                       
handlerList.add(handler);
                                                else
                                                        logOrThrowException("No 
handler for type: " + field.getType().getName(), null);
                                        } catch (final Throwable ex) {
-                                               logOrThrowException("tunable 
field intercept failed for "
-                                                           + field.toString(), 
ex);
+                                               logOrThrowException("tunable 
field intercept failed for " + field.toString(), ex);
                                        }
+
+                               // Evaluate fields for ContainsTunables 
annotation. If the field
+                               // is annotated, then get the object from the 
field and evaluate
+                               // *it* for tunables.
+                               } else if 
(field.isAnnotationPresent(ContainsTunables.class)) {
+                                       try { 
+                                               Object tunableContainer = 
field.get(obj);
+                                               if ( 
!handlerMap.containsKey(tunableContainer) )
+                                                       handlerList.addAll( 
loadTunables(tunableContainer) );
+                                       } catch (final Throwable ex) {
+                                               
logOrThrowException("ContainsTunables field intercept failed for " + 
field.toString(), ex);
+                                       }
                                }
                        }
 
-                       guiProviderMap.clear();
-                       
                        // Find each public method in the class.
                        for (final Method method : obj.getClass().getMethods()) 
{
                                // See if the method is annotated as a Tunable.
@@ -139,43 +143,30 @@
                                                if (handler == null) {
                                                        
logOrThrowException("Failed to create a handler for " + setter.getName() + 
"()!",null);
                                                } else {
-                                                       
handlerList.put("getset" + rootName, handler);
+                                                       
handlerList.add(handler);
                                                }
                                        } catch (Throwable t) {
                                                logOrThrowException("tunable 
method intercept failed for " + method.toString(), t);
                                        }
-                               }else if 
(method.isAnnotationPresent(ProvidesTitle.class)) {
+                               } else if 
(method.isAnnotationPresent(ProvidesTitle.class)) {
                                        if 
(!String.class.isAssignableFrom(method.getReturnType())) {
                                                throw new 
IllegalArgumentException(method.getName() + " annotated with @ProvidesTitle 
must return String!");
                                        } else if 
(method.getParameterTypes().length != 0) {
                                                throw new 
IllegalArgumentException(method.getName() + " annotated with @ProvidesTitle 
must take 0 arguments!");
                                        } else {
                                                if 
(titleProviderMap.containsKey(obj)) {
-                                                       throw new 
IllegalArgumentException("Classes must have at most a single @ProvidesTitle 
annotated method but + "
-                                                               + 
method.getDeclaringClass().getName() + " has more than one!");
+                                                       throw new 
IllegalArgumentException("Classes must have at most one @ProvidesTitle 
annotated method but " + method.getDeclaringClass().getName() + " has more than 
one!");
                                                }
                                                titleProviderMap.put(obj, 
method);
                                        }
                                }
                        }
-
-                       handlerMap.put(obj, handlerList);
                }
+
+               return handlerList;
        }
        
        
-       private boolean isJPanelOrJPanelDescendent(final Class c) {
-               Class c0 = c;
-               while (c0 != null && c0 != Object.class) {
-                       // hack so that we don't need to import swing
-                       if (c0.getName().equals( "javax.swing.JPanel" ))
-                               return true;
-                       c0 = c0.getSuperclass();
-               }
-
-               return false;
-       }
-
        private boolean isValidGetter(final Method getterCandidate) {
                // Make sure we're not returning "void":
                try {
@@ -216,14 +207,11 @@
         * @return The map that contains all the {@link TunableHandler} objects 
that have been 
         * found to process the fields and methods annotated with {@link 
Tunable}.
         */
-       public final Map<String, T> getHandlers(final Object o) {
+       public final List<T> getHandlers(final Object o) {
                if (o == null)
                        return null;
 
-               // this is a no-op if tunables have already been loaded
-               loadTunables(o);
-
-               return handlerMap.get(o);
+               return loadTunables(o);
        }
 
        /** 
@@ -234,8 +222,17 @@
         */
        public boolean hasTunables(final Object o) {
                for (final Field field : o.getClass().getFields()) {
-                       if (field.isAnnotationPresent(Tunable.class))
+                       if (field.isAnnotationPresent(Tunable.class)) {
                                return true;
+                       } else if 
(field.isAnnotationPresent(ContainsTunables.class)) {
+                               try { 
+                                       Object tunableContainer = field.get(o);
+                                       return hasTunables(tunableContainer);
+                               } catch (final Throwable ex) {
+                                       logger.debug("ContainsTunables field 
intercept failed for " + field.toString(), ex);
+                                       return false;
+                               }
+                       }
                }
                for (final Method method : o.getClass().getMethods()) {
                        if (method.isAnnotationPresent(Tunable.class))

Added: 
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/ContainsTunables.java
===================================================================
--- 
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/ContainsTunables.java 
                            (rev 0)
+++ 
core3/api/trunk/work-api/src/main/java/org/cytoscape/work/ContainsTunables.java 
    2012-03-16 21:43:48 UTC (rev 28569)
@@ -0,0 +1,38 @@
+package org.cytoscape.work;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * An annotation designed to signal that the annotated field contains
+ * fields and methods that are annotated with the Tunable annotation.
+ * The goal is to allow rich tunable collections to be created by
+ * combining the tunables found in fields annotated with ContainsTunables. 
+ * <br/>
+ * For example, given classes A and B:
+ * <p><pre><code>
+ *
+ * public class A {
+ *    @Tunable
+ *    public int value = 5;
+ * }
+ *
+ * public class B {
+ *    @ContainsTunable
+ *    public A a = new A(); 
+ *    @Tunable
+ *    public String name = "homer";
+ * }
+ *
+ * </code></pre></p>
+ * When class B is evaluated for Tunables, it should display a tunable
+ * for both the "name" and "value" fields.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface ContainsTunables {
+}

Modified: 
core3/api/trunk/work-api/src/test/java/org/cytoscape/work/AbstractTunableInterceptorTest.java
===================================================================
--- 
core3/api/trunk/work-api/src/test/java/org/cytoscape/work/AbstractTunableInterceptorTest.java
       2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/api/trunk/work-api/src/test/java/org/cytoscape/work/AbstractTunableInterceptorTest.java
       2012-03-16 21:43:48 UTC (rev 28569)
@@ -58,8 +58,8 @@
 
        @Test
        public final void testLoadTunables() {
-               interceptor.loadTunables(hasAnnotatedField);
-               interceptor.loadTunables(hasAnnotatedSetterAndGetterMethods);
+               interceptor.getHandlers(hasAnnotatedField);
+               interceptor.getHandlers(hasAnnotatedSetterAndGetterMethods);
        }
 
        @Test
@@ -70,44 +70,74 @@
 
        @Test(expected=IllegalArgumentException.class)
        public final void testInvalidGetter() {
-               interceptor.loadTunables(new HasInvalidGetter());
+               interceptor.getHandlers(new HasInvalidGetter());
        }
 
        @Test(expected=IllegalArgumentException.class)
        public final void testInvalidGetterReturnType() {
-               interceptor.loadTunables(new HasInvalidGetterReturnType());
+               interceptor.getHandlers(new HasInvalidGetterReturnType());
        }
 
        @Test(expected=IllegalArgumentException.class)
        public final void testMisnamedGetter() {
-               interceptor.loadTunables(new HasMisnamedGetter());
+               interceptor.getHandlers(new HasMisnamedGetter());
        }
 
        @Test(expected=IllegalArgumentException.class)
        public final void testInvalidSetter() {
-               interceptor.loadTunables(new HasInvalidSetter());
+               interceptor.getHandlers(new HasInvalidSetter());
        }
 
        @Test(expected=IllegalArgumentException.class)
        public final void testInvalidAnnotatedType() {
-               interceptor.loadTunables(new HasInvalidAnotatedType());
+               interceptor.getHandlers(new HasInvalidAnotatedType());
        }
 
 
        @Test(expected=IllegalArgumentException.class)
        public final void testSetterAnnotatedInsteadOfGetter() {
-               interceptor.loadTunables(new SetterAnnotatedInsteadOfGetter());
+               interceptor.getHandlers(new SetterAnnotatedInsteadOfGetter());
        }
 
        @Test
        public final void testInheritedField() {
                assertTrue( interceptor.hasTunables( new ExtendedFieldClass() ) 
);
+               assertTrue( interceptor.getHandlers( new ExtendedFieldClass() 
).size() > 0 );
        }
 
        @Test
        public final void testInheritedMethod() {
                assertTrue( interceptor.hasTunables( new ExtendedMethodClass() 
) );
+               assertTrue( interceptor.getHandlers( new ExtendedMethodClass() 
).size() > 0 );
        }
+
+       @Test
+       public final void testJustContainsTunables() {
+               Object o = new ContainsTunablesClass();
+               assertTrue( interceptor.hasTunables( o ) );
+               assertTrue( interceptor.getHandlers(o).size() > 0 );
+       }
+
+       @Test
+       public final void testContainsTunablesAndTunables() {
+               Object o = new ContainsTunablesAndTunablesClass();
+               assertTrue( interceptor.hasTunables( o ) );
+               assertTrue( interceptor.getHandlers(o).size() > 1 );
+       }
+
+       @Test
+       public final void testContainsTunablesRecusion() {
+               Object o = new ContainsTunablesClass2();
+               assertTrue( interceptor.hasTunables( o ) );
+               assertTrue( interceptor.getHandlers(o).size() > 0 );
+       }
+
+       @Test(expected=IllegalArgumentException.class)
+       public final void testContainsTunablesUninitialized() {
+               Object o = new UninitializedContainsTunablesClass();
+               interceptor.getHandlers(o);
+       }
+
 }
 
 class FakeTunableHandler extends AbstractTunableHandler {
@@ -120,27 +150,11 @@
        public void handle() {}
 }
 
-
-//class SimpleHandlerFactory implements 
TunableHandlerFactory<AbstractTunableHandler> {
-//     public AbstractTunableHandler getHandler(final Field field, final 
Object instance, final Tunable tunable) {
-//             return new FakeTunableHandler(field, instance, tunable);
-//     }
-//
-//     public AbstractTunableHandler getHandler(final Method setter, final 
Method getter, final Object instance, final Tunable tunable) {
-//             return new FakeTunableHandler(setter, getter, instance, 
tunable);
-//     }
-//}
-
-
 class ConcreteTunableInterceptor extends AbstractTunableInterceptor {
 
        public boolean validateAndWriteBackTunables(Object... objs) {
                return true;
        }
-
-       public boolean execUI(Object... obs) {
-               return true;
-       }
 }
 
 
@@ -202,3 +216,33 @@
 
 class ExtendedMethodClass extends BaseMethodClass {
 }
+
+class ContainsTunablesClass {
+       @ContainsTunables
+       public BaseMethodClass bmc = new BaseMethodClass();
+}
+
+
+class ContainsTunablesClass2 {
+       @ContainsTunables
+       public ContainsTunablesClass ctc = new ContainsTunablesClass();
+}
+
+class ContainsTunablesAndTunablesClass {
+
+       @ContainsTunables
+       public BaseMethodClass bmc = new BaseMethodClass();
+
+       @Tunable
+       public int getXValue() { return 0; }
+
+       public void setXValue(int a) { };
+
+       @Tunable
+       public int yValue;
+}
+
+class UninitializedContainsTunablesClass {
+       @ContainsTunables
+       public BaseMethodClass bmc; 
+}

Modified: 
core3/impl/trunk/command-executor-impl/src/main/java/org/cytoscape/command/internal/tunables/CommandTunableInterceptorImpl.java
===================================================================
--- 
core3/impl/trunk/command-executor-impl/src/main/java/org/cytoscape/command/internal/tunables/CommandTunableInterceptorImpl.java
     2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/impl/trunk/command-executor-impl/src/main/java/org/cytoscape/command/internal/tunables/CommandTunableInterceptorImpl.java
     2012-03-16 21:43:48 UTC (rev 28569)
@@ -1,6 +1,5 @@
 package org.cytoscape.command.internal.tunables;
 
-
 import java.util.Map;
 
 import org.cytoscape.work.AbstractTunableInterceptor;
@@ -23,8 +22,7 @@
                        // Get the handlers for the tunables in the Task.  The
                        // key is the name of the tunable and the value is the 
handler
                        // for that tunable.
-                       Map<String,StringTunableHandler> handlers = 
getHandlers(o);
-                       for ( StringTunableHandler h : handlers.values() ) {
+                       for ( StringTunableHandler h : getHandlers(o) ) {
 
                                // Give the handler the arg string and let it 
do its thing,
                                // which will hopefully be: set the tunable 
value based on

Modified: 
core3/impl/trunk/swing-application-impl/src/main/java/org/cytoscape/internal/commands/ArgRecorder.java
===================================================================
--- 
core3/impl/trunk/swing-application-impl/src/main/java/org/cytoscape/internal/commands/ArgRecorder.java
      2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/impl/trunk/swing-application-impl/src/main/java/org/cytoscape/internal/commands/ArgRecorder.java
      2012-03-16 21:43:48 UTC (rev 28569)
@@ -15,8 +15,7 @@
 
        public List<String> findArgs(Object o) {
                List<String> desc = new ArrayList<String>();
-               Map<String,ArgHandler> handlers = getHandlers(o);
-               for (final ArgHandler p : handlers.values())
+               for (final ArgHandler p : getHandlers(o))
                        desc.add( p.getDesc() );
                return desc;
        }

Modified: 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/submenu/SubmenuTunableMutator.java
===================================================================
--- 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/submenu/SubmenuTunableMutator.java
  2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/submenu/SubmenuTunableMutator.java
  2012-03-16 21:43:48 UTC (rev 28569)
@@ -31,8 +31,7 @@
                else
                        return null;
 
-               Map<String,SubmenuTunableHandler> handlers = getHandlers(tf);
-               for ( SubmenuTunableHandler handler : handlers.values() ) {
+               for ( SubmenuTunableHandler handler : getHandlers(tf) ) {
                        handler.setExecutionParams(dtm,tf);
                        handler.handle();
                        return handler.getSubmenuItem();

Modified: 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/sync/SyncTunableMutator.java
===================================================================
--- 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/sync/SyncTunableMutator.java
        2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/sync/SyncTunableMutator.java
        2012-03-16 21:43:48 UTC (rev 28569)
@@ -27,9 +27,8 @@
        
        @Override
        public boolean validateAndWriteBack(Object o) {
-               final Map<String, SyncTunableHandler> handlers = getHandlers(o);
                
-               for (SyncTunableHandler handler : handlers.values()) {
+               for (SyncTunableHandler handler : getHandlers(o)) {
                        handler.setValueMap(map);
                        handler.handle();
                }

Modified: 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/tunables/JPanelTunableMutator.java
===================================================================
--- 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/tunables/JPanelTunableMutator.java
  2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/impl/trunk/work-swing-impl/impl/src/main/java/org/cytoscape/work/internal/tunables/JPanelTunableMutator.java
  2012-03-16 21:43:48 UTC (rev 28569)
@@ -87,7 +87,7 @@
        /** {@inheritDoc} */
        public boolean validateAndWriteBack(Object objectWithTunables) {
 
-               List<GUITunableHandler> handlers = 
findHandlers(objectWithTunables); 
+               List<GUITunableHandler> handlers = 
getHandlers(objectWithTunables); 
 
                for (final GUITunableHandler h : handlers)
                        h.handle();
@@ -114,7 +114,7 @@
                else
                        ++otherCount;
 
-               List<GUITunableHandler> handlers = 
findHandlers(objectWithTunables); 
+               List<GUITunableHandler> handlers = 
getHandlers(objectWithTunables); 
 
                // Sanity check:
                if (factoryCount > 0) {
@@ -330,20 +330,6 @@
                return true;
        }
 
-       private List<GUITunableHandler> findHandlers(Object objectWithTunables) 
{
-               List<GUITunableHandler> handlers = new 
ArrayList<GUITunableHandler>();
-
-               // the call to getHandlers will populate guiProviderMap
-               // note that map may be empty!
-               Map<String, GUITunableHandler> tunableHandlerMap = 
getHandlers(objectWithTunables);
-
-               
-               if ( tunableHandlerMap != null && !tunableHandlerMap.isEmpty() )
-                       handlers.addAll(tunableHandlerMap.values());
-
-               return handlers;
-       }
-       
        protected String getTitle(Object objectWithTunables) {
                Method method = titleProviderMap.get(objectWithTunables);
                if (method != null) {

Added: 
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/JustTunables.java
===================================================================
--- 
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/JustTunables.java
                          (rev 0)
+++ 
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/JustTunables.java
  2012-03-16 21:43:48 UTC (rev 28569)
@@ -0,0 +1,15 @@
+
+package org.cytoscape.internal.test.tunables;
+
+
+import org.cytoscape.work.Tunable;
+
+
+public class JustTunables {
+
+       @Tunable(description="Enter a value:",groups="Examples of Tunables From 
Other Object")
+       public int value = 5;
+
+       @Tunable(description="Enter a name",groups="Examples of Tunables From 
Other Object")
+       public String name = "Scooter";
+}

Modified: 
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/ScootersTunableTask.java
===================================================================
--- 
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/ScootersTunableTask.java
   2012-03-16 21:37:08 UTC (rev 28568)
+++ 
core3/support/trunk/task-testing-impl/src/main/java/org/cytoscape/internal/test/tunables/ScootersTunableTask.java
   2012-03-16 21:43:48 UTC (rev 28569)
@@ -5,6 +5,7 @@
 import org.cytoscape.work.AbstractTask;
 import org.cytoscape.work.TaskMonitor;
 import org.cytoscape.work.Tunable;
+import org.cytoscape.work.ContainsTunables;
 import org.cytoscape.work.util.ListSingleSelection;
 
 
@@ -53,6 +54,9 @@
                return aggregationType;
        }
 
+       @ContainsTunables
+       public JustTunables someSortOfConfigObject = new JustTunables();
+
        public void setAggregationType(ListSingleSelection<String> input) {
                // Ignore because ListSingleSelection is set in the handler and 
not here.
        }

-- 
You received this message because you are subscribed to the Google Groups 
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/cytoscape-cvs?hl=en.

Reply via email to