View Javadoc

1   /*
2    * Copyright 2006 Outsource Cafe, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the 'License')
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an 'AS IS' BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.javagen.agile.core.visitor;
17  
18  import java.beans.BeanInfo;
19  import java.beans.Introspector;
20  import java.beans.PropertyDescriptor;
21  import java.beans.PropertyEditor;
22  import java.beans.PropertyEditorManager;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.Method;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.javagen.agile.core.annotation.DefaultValue;
31  import org.javagen.agile.core.model.Model;
32  
33  /***
34   * Walk a model tree and set all non-null values to <code>DefaultValue</code> if present.
35   * Requires property types to have a String constructor or PropertyEditor defined.
36   *
37   * @see org.javagen.agile.core.annotation.DefaultValue
38   * @see PropertyEditorManager
39   * @author Richard Easterling
40   */
41  public class DefaultValueVisitor extends DefaultVisitor {
42  
43      Map<Class<?>, List<DefaultSetter>> cachedSetters = new HashMap<Class<?>, List<DefaultSetter>>();
44      
45      ////////////////////////////////////////////////////////////////////////////
46      // Visitor interface:
47      ////////////////////////////////////////////////////////////////////////////
48  
49      public void visit(Model model) {
50          List<DefaultSetter> setters = cachedSetters.get(model.getClass());
51          if (setters==null) {
52              setters = load(model);
53              cachedSetters.put(model.getClass(), setters);
54          }
55          for(DefaultSetter setter : setters) {
56              setter.set(model);
57          }
58          //System.out.println("set "+setters.size()+" defaultValues on model " +model.getId());
59      }
60      
61      ////////////////////////////////////////////////////////////////////////////
62      // utility methods:
63      ////////////////////////////////////////////////////////////////////////////
64  
65      private List<DefaultSetter> load(Model model) {
66          List<DefaultSetter> setters = new ArrayList<DefaultSetter>();
67          try {
68              BeanInfo beanInfo = Introspector.getBeanInfo(model.getClass());
69              for(PropertyDescriptor propDesc : beanInfo.getPropertyDescriptors()) {
70                  Class<?> type = propDesc.getPropertyType();
71                  Method getMethod = propDesc.getReadMethod();
72                  DefaultValue defaultValue = getMethod==null ? null : getMethod.getAnnotation(DefaultValue.class);
73                  //System.out.println(propDesc.getName()+": "+(getMethod==null ? "null":getMethod.getName()));
74                  if (defaultValue==null)
75                      continue;
76                  Object value = defaultValue.value();
77                  if ( ! type.getClass().equals(String.class) ) {
78                      PropertyEditor propEditor = PropertyEditorManager.findEditor(type);
79                      if (propEditor!=null) {
80                          propEditor.setAsText(value.toString());
81                          value = propEditor.getValue();
82                      } else {
83                          Constructor<?> constructor = type.getConstructor(new Class[]{String.class});
84                          if (constructor!=null) {
85                              value = constructor.newInstance(new Object[]{value});
86                          } else {
87                              throw new RuntimeException("can't set @DefaultValue("+value+") on "+model.getClass().getName()+"."+propDesc.getName()+" no PropertyEditor registered or String constructor found.");
88                          }
89                      }
90                  }
91                  Method setMethod = propDesc.getWriteMethod();
92                  if (setMethod==null)
93                      throw new RuntimeException("can't set @DefaultValue("+value+") on "+model.getClass().getName()+"."+propDesc.getName()+" no PropertyEditor registered or setter method found.");
94                  setters.add( new DefaultSetter(value, setMethod, getMethod));
95              }
96              //System.out.println("laod "+setters.size()+" defaultValues for " +model.getClass().getName());
97              return setters;
98          } catch (Exception e) {
99              throw new RuntimeException(e);
100         }
101     }
102     
103     ////////////////////////////////////////////////////////////////////////////
104     // cache element class:
105     ////////////////////////////////////////////////////////////////////////////
106     
107     static class DefaultSetter {
108         private Object[] defaultValueArg;
109         private Method setMethod;
110         private Method getMethod;
111         static final Object[] NO_ARGS = new Object[0];
112 
113         public DefaultSetter(Object defaultValue, Method setMethod, Method getMethod) {
114             this.defaultValueArg = new Object[]{defaultValue};
115             this.setMethod = setMethod;
116             this.getMethod = getMethod;
117         }
118         
119         public void set(Model model) {
120             try {
121                 if (getMethod.invoke(model, NO_ARGS)==null)
122                     setMethod.invoke(model, defaultValueArg);
123             } catch (Exception e) {
124                 throw new RuntimeException(e);
125             }
126         }
127     }
128     
129 
130 }