Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: echopoint/stylesheet/StyleSheetIntrospector.java


1   package echopoint.stylesheet;
2   /* 
3    * This file is part of the Echo Point Project.  This project is a collection
4    * of Components that have extended the Echo Web Application Framework.
5    *
6    * EchoPoint is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as published by
8    * the Free Software Foundation; either version 2 of the License, or
9    * (at your option) any later version.
10   *
11   * EchoPoint is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public License
17   * along with Echo Point; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  
21  import java.beans.IntrospectionException;
22  import java.lang.reflect.Modifier;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Stack;
28  
29  import echopoint.util.collections.ConcurrentReaderHashMap;
30  
31  /**
32   * The <code>StyleSheetIntrospector</code> is used to introspect on a given 
33   * Component class and find out style attribute information on that class.
34   * <p>
35   * The <code>StyleSheetIntrospector</code> looks for a Components's "style info" 
36   * support class that implements <code>StyleInfo</code>
37   * <p>
38   * In much the same way as the java.beans.Introspector looks for BeanInfo classes
39   * the <code>StyleSheetIntrospector</code> will first look for a class named
40   * <i>componentClass</i>StyleInfo in the same package as component which implements
41   * the echopoint.stylesheet.StyleInfo interface.
42   * <p>
43   * If it cant find that class, it will look for the first public static 
44   * nested class inside the Component class that implements the 
45   * echopoint.stylesheet.StyleInfo interface.
46   * <p>
47   * So for example if you component class is call "x.y.z.BlueBell" then the
48   * <code>StyleSheetIntrospector</code> will look for a class
49   * called "x.y.z.BlueBellStyleInfo".
50   * <p>
51   * As in:
52   * <code>
53   * public class BlueBellStyleInfo implements echopoint.stylesheet.StyleInfo {<br>
54   *     ...<br>
55   * }<br>
56   * </code>
57   * If it cannot that, it will look for a nested class within 
58   * "x.y.z.BlueBell" that is public, static and that 
59   * implements <code>echopoint.stylesheet.StyleInfo</code>.
60   * <p>
61   * As in:
62   * <code>
63   * public class BlueBell extends nextapp.echo.Component {<br>
64   *     public static NestedStyleInfo implements echopoint.stylesheet.StyleInfo {<br>
65   *       ...<br>
66   *     }<br>
67   *     <br>
68   *     ...<br>
69   * }<br>
70   * </code>
71   * <p>
72   * After the above processing is complete, the introspector will examine the interfaces that the class
73   * implements.  It will introspect inside all of its interfaces for public nested static
74   * class that implements <code>echopoint.stylesheet.StyleInfo</code>.  The union of these
75   * StyleInfo implementing classes will then be returned for that component class.
76   * <p>
77   * For example given :
78   * <code><br>
79   *     public class MyComponent extends Component implements Slip, Slop, Slap {<br>
80   *     ...<br>
81   * </code>
82   * The introspector will examine the Slip, Slop Slap interfaces to see if they
83   * have nested public static StyleInfo classes.  Any sub-interfaces that these
84   * interfaces may themselves extended will also be searched.  The union of these
85   * StyleInfo objects will be returned as the StyleInfo for the original component
86   * 
87   *    
88   */
89  public class StyleSheetIntrospector {
90  
91    /** this hold our global cache of Class --> StyleInfo */
92    private static Map styleInfoCache = new ConcurrentReaderHashMap();
93  
94  
95    /**
96     * Adds a StyleInfo object for the specified clazz into the global cache.  Once 
97     * added it can be quickly gathered again.  In general you will not need
98     * to call this function because it all results of the getStyleInfo() 
99     * method are always globally cached.
100    *   
101    * @param clazz - the class that the StyleInfo applies
102    * @param styleInfo - the styleInfo in question
103    */
104   public static void addCachedStyleInfo(Class clazz, StyleInfo styleInfo) {
105     if (styleInfo != null && clazz != null)
106       styleInfoCache.put(clazz,styleInfo);
107   }
108   
109   /**
110    * Flushes the global cache of StyleInfo information.
111    * 
112    */
113   public static void flushCachedStyleInfo() {
114     styleInfoCache.clear();
115   }
116   /**
117    * Introspects a class and learns about the style attributes it supports.  
118    *  
119    * @param searchClass - the component class that is to be introspected 
120    * @return StyleInfo - a StyleInfo object or null if no info can be found
121    * @throws IntrospectionException - if an exception occurs during introspection.
122    */
123   public static StyleInfo getStyleInfo(Class searchClass) throws IntrospectionException {
124     if (searchClass == null)
125       throw new IntrospectionException("The component class must be non null");
126 
127     StyleInfo styleInfo = null;
128     styleInfo = (StyleInfo) styleInfoCache.get(searchClass);
129     if (styleInfo != null)
130       return styleInfo;
131     
132     // find class style info  
133     styleInfo = findClassStyleInfo(searchClass);
134     // and possibly combine it with interface style info
135     styleInfo = combineInterfaceStyleInfo(searchClass,styleInfo);
136     // and add to our cache
137     addCachedStyleInfo(searchClass,styleInfo);
138     return styleInfo;
139   }
140 
141   /**
142    * Does the multistage lookup of StyleInfo data.
143    */
144   private static StyleInfo findClassStyleInfo(Class searchClass) {
145     String styleInfoClassName = searchClass.getName() + "StyleInfo";
146     Class styleInfoClass = null;
147     
148     if (! searchClass.isInterface()) {
149       // look for top level xxxStyleInfo class
150       try {
151         styleInfoClass = Class.forName(styleInfoClassName);
152         int mods = styleInfoClass.getModifiers();
153         if (!Modifier.isPublic(mods))
154           return null;
155       } catch (ClassNotFoundException e) {
156         styleInfoClass = null;
157       }
158       if (styleInfoClass != null && StyleInfo.class.isAssignableFrom(styleInfoClass)) {
159         try {
160           return (StyleInfo) styleInfoClass.newInstance();
161         } catch (InstantiationException e1) {
162           return null;
163         } catch (IllegalAccessException e1) {
164           return null;
165         }
166       }
167     }
168     //
169     // okay. lets look for nested static inner classes
170     // look for the any nested StyleInfo implementing class
171     // 
172     StyleInfo styleInfo = lookForNestedStyleInifo(searchClass);
173     if (styleInfo != null)  
174       return styleInfo;
175       
176     return null;
177   }
178   
179   /**
180    * Called to get an interface StyleInfo.  If the there are
181    * no StyleInfo classes in the interfaces then the original
182    * classStyleInfo object is return.
183    */
184   private static StyleInfo combineInterfaceStyleInfo(Class searchClass, StyleInfo classStyleInfo) {
185     //
186     // check the interfaces that the class might implement
187     //
188     StyleAttrDescriptor[] descs;
189     StyleInfo styleInfo;
190     Class[] classes = searchClass.getInterfaces();
191     int descriptorCount = 0;
192     List descriptorList = new ArrayList();
193     Stack stack = new Stack();
194     stack.push(classes);
195     while (! stack.isEmpty()) {
196       classes = (Class[]) stack.pop();
197       for (int i = 0; i < classes.length; i++) {
198         styleInfo = lookForNestedStyleInifo(classes[i]);
199         if (styleInfo != null) {
200           descs = styleInfo.getStyleDescriptors();
201           if (descs != null) {
202             descriptorCount += descs.length;
203             descriptorList.add(descs);
204           }
205         }
206         // find child interfaces
207         if (classes[i].getInterfaces().length > 0)
208           stack.push(classes[i].getInterfaces());
209       }
210     }
211     //
212     // there are no interfaces with StyleInfo
213     if (descriptorCount == 0)
214       return classStyleInfo;
215     
216     // combine our previous class StyleInfo    
217     if (classStyleInfo != null) {
218       descs = classStyleInfo.getStyleDescriptors();
219       if (descs != null) {
220         descriptorCount += descs.length;
221         descriptorList.add(descs);
222       }
223     }
224     // need to convert the union of all the interfaces with descriptors into the one
225     // StyleInfo object;
226     int i = 0;  
227     final StyleAttrDescriptor[] combinedDescs = new StyleAttrDescriptor[descriptorCount];
228     for (Iterator iter = descriptorList.iterator(); iter.hasNext();) {
229       StyleAttrDescriptor[] descArr = (StyleAttrDescriptor[]) iter.next();
230       for (int j = 0; j < descArr.length; j++) {
231         combinedDescs[i] = descArr[j]; 
232         i++;
233       }
234     }
235     styleInfo = new StyleInfo() {
236       public StyleAttrDescriptor[] getStyleDescriptors() {
237         return combinedDescs;
238       }
239     };
240     return styleInfo;
241   }
242   
243   /**
244    * Looks inside a class for all the nested StyleInfo classes 
245    */
246   private static StyleInfo lookForNestedStyleInifo(Class searchClass) {
247     StyleInfo styleInfo = null;
248     Class[] classes = searchClass.getDeclaredClasses();
249     for (int i = 0; i < classes.length; i++) {
250       styleInfo = isNestedStyleInfo(classes[i]);
251       if (styleInfo != null) {
252         return styleInfo;
253       }
254     }
255     return null;
256   }
257   
258   /**
259    * Test a given class as to whether it implements StyleInfo
260    * and whether its public and static.
261    * 
262    * @param searchClass
263    * @return
264    */
265   private static StyleInfo isNestedStyleInfo(Class searchClass) {
266     int mods = searchClass.getModifiers();
267     if (StyleInfo.class.isAssignableFrom(searchClass) && Modifier.isPublic(mods) && Modifier.isStatic(mods)) {
268       try {
269         return (StyleInfo) searchClass.newInstance();
270       } catch (InstantiationException e1) {
271         return null;
272       } catch (IllegalAccessException e1) {
273         return null;
274       }
275     }
276     return null;
277   }
278 }