View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.facelets;
20  
21  import java.beans.BeanInfo;
22  import java.beans.IntrospectionException;
23  
24  import java.beans.Introspector;
25  import java.beans.PropertyDescriptor;
26  
27  import java.lang.reflect.InvocationTargetException;
28  import java.lang.reflect.Method;
29  
30  import java.util.ArrayList;
31  import java.util.HashMap;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.WeakHashMap;
36  import java.util.logging.Level;
37  
38  import javax.faces.view.facelets.FaceletContext;
39  import javax.faces.view.facelets.MetaRule;
40  import javax.faces.view.facelets.MetaRuleset;
41  import javax.faces.view.facelets.Metadata;
42  import javax.faces.view.facelets.MetadataTarget;
43  import javax.faces.view.facelets.Tag;
44  import javax.faces.view.facelets.TagAttribute;
45  import javax.faces.view.facelets.TagAttributeException;
46  import javax.faces.view.facelets.TagConfig;
47  import javax.faces.view.facelets.TagException;
48  import javax.faces.view.facelets.TagHandler;
49  
50  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
51  
52  
53  /**
54   * A base tag for wiring state to an object instance based on rules populated at
55   * the time of creating a MetaRuleset.
56   *
57   * Implementation copied from Facelets 1.1.14, as it got hidden by JSF 2.0
58   * 
59   * @author Jacob Hookom
60   */
61  
62  public abstract class MetaTagHandler extends TagHandler {
63      private Class lastType = Object.class;
64  
65      private Metadata mapper;
66  
67      public MetaTagHandler(TagConfig config) {
68          super(config);
69      }
70  
71      /**
72       * Extend this method in order to add your own rules.
73       *
74       * @param type
75       * @return
76       */
77      protected MetaRuleset createMetaRuleset(Class type) {
78          assert (type != null);
79          return new MetaRulesetImpl(this.tag, type);
80      }
81  
82      /**
83       * Invoking/extending this method will cause the results of the created
84       * MetaRuleset to auto-wire state to the passed instance.
85       *
86       * @param ctx
87       * @param instance
88       */
89      protected void setAttributes(FaceletContext ctx, Object instance) {
90          if (instance != null) {
91              Class type = instance.getClass();
92              if (mapper == null || !this.lastType.equals(type)) {
93                  this.lastType = type;
94                  this.mapper = this.createMetaRuleset(type).finish();
95              }
96              this.mapper.applyMetadata(ctx, instance);
97          }
98      }
99  
100 
101     private static class BeanPropertyTagRule extends MetaRule {
102 
103         final static class LiteralPropertyMetadata extends Metadata {
104 
105             private final Method method;
106 
107             private final TagAttribute attribute;
108 
109             private Object[] value;
110 
111             public LiteralPropertyMetadata(Method method,
112                                            TagAttribute attribute) {
113                 this.method = method;
114                 this.attribute = attribute;
115             }
116 
117             public void applyMetadata(FaceletContext ctx, Object instance) {
118                 if (value == null) {
119                     String str = this.attribute.getValue();
120                     value = new Object[] { ctx.getExpressionFactory().coerceToType(str,
121                                                        method.getParameterTypes()[0]) };
122                 }
123                 try {
124                     method.invoke(instance, this.value);
125                 } catch (InvocationTargetException e) {
126                     throw new TagAttributeException(this.attribute,
127                                                     e.getCause());
128                 } catch (Exception e) {
129                     throw new TagAttributeException(this.attribute, e);
130                 }
131             }
132 
133         }
134 
135         final static class DynamicPropertyMetadata extends Metadata {
136 
137             private final Method method;
138 
139             private final TagAttribute attribute;
140 
141             private final Class type;
142 
143             public DynamicPropertyMetadata(Method method,
144                                            TagAttribute attribute) {
145                 this.method = method;
146                 this.type = method.getParameterTypes()[0];
147                 this.attribute = attribute;
148             }
149 
150             public void applyMetadata(FaceletContext ctx, Object instance) {
151                 try {
152                     this.method.invoke(instance,
153                                        new Object[] { this.attribute.getObject(ctx,
154                                                                                this.type) });
155                 } catch (InvocationTargetException e) {
156                     throw new TagAttributeException(this.attribute,
157                                                     e.getCause());
158                 } catch (Exception e) {
159                     throw new TagAttributeException(this.attribute, e);
160                 }
161             }
162         }
163 
164         public final static BeanPropertyTagRule Instance =
165             new BeanPropertyTagRule();
166 
167         public Metadata applyRule(String name, TagAttribute attribute,
168                                   MetadataTarget meta) {
169             Method m = meta.getWriteMethod(name);
170 
171             // if the property is writable
172             if (m != null) {
173                 if (attribute.isLiteral()) {
174                     return new LiteralPropertyMetadata(m, attribute);
175                 } else {
176                     return new DynamicPropertyMetadata(m, attribute);
177                 }
178             }
179 
180             return null;
181         }
182 
183     }
184 
185     private static class MetaRulesetImpl extends MetaRuleset {
186 
187         private final static WeakHashMap metadata = new WeakHashMap();
188 
189         static final private TrinidadLogger log =
190             TrinidadLogger.createTrinidadLogger(MetaRulesetImpl.class);
191 
192         private final Tag tag;
193 
194         private final Class type;
195 
196         private final Map attributes;
197 
198         private final List mappers;
199 
200         private final List rules;
201 
202         public MetaRulesetImpl(Tag tag, Class type) {
203             this.tag = tag;
204             this.type = type;
205             this.attributes = new HashMap();
206             this.mappers = new ArrayList();
207             this.rules = new ArrayList();
208 
209             // setup attributes
210             TagAttribute[] attrs = this.tag.getAttributes().getAll();
211             for (int i = 0; i < attrs.length; i++) {
212                 attributes.put(attrs[i].getLocalName(), attrs[i]);
213             }
214 
215             // add default rules
216             this.rules.add(BeanPropertyTagRule.Instance);
217         }
218 
219         public MetaRuleset ignore(String attribute) {
220             assert (attribute != null);
221             this.attributes.remove(attribute);
222             return this;
223         }
224 
225         public MetaRuleset alias(String attribute, String property) {
226             assert (attribute != null);
227             assert (property != null);
228             TagAttribute attr =
229                 (TagAttribute)this.attributes.remove(attribute);
230             if (attr != null) {
231                 this.attributes.put(property, attr);
232             }
233             return this;
234         }
235 
236         public MetaRuleset add(Metadata mapper) {
237             assert (mapper != null);
238             if (!this.mappers.contains(mapper)) {
239                 this.mappers.add(mapper);
240             }
241             return this;
242         }
243 
244         public MetaRuleset addRule(MetaRule rule) {
245             assert (rule != null);
246             this.rules.add(rule);
247             return this;
248         }
249 
250         private final MetadataTarget getMetadataTarget() {
251             String key = this.type.getName();
252             MetadataTarget meta = (MetadataTarget)metadata.get(key);
253             if (meta == null) {
254                 try {
255                     meta = new MetadataTargetImpl(type);
256                 } catch (IntrospectionException e) {
257                     throw new TagException(this.tag, "Error Creating TargetMetadata", e);
258                 }
259                 metadata.put(key, meta);
260             }
261             return meta;
262         }
263 
264         public Metadata finish() {
265             if (!this.attributes.isEmpty()) {
266                 if (this.rules.isEmpty()) {
267                     if (log.isLoggable(Level.SEVERE)) {
268                         for (Iterator itr =
269                              this.attributes.values().iterator();
270                              itr.hasNext(); ) {
271                             log.severe(itr.next() +
272                                        " Unhandled by MetaTagHandler for type " +
273                                        this.type.getName());
274                         }
275                     }
276                 } else {
277                     MetadataTarget target = this.getMetadataTarget();
278                     // now iterate over attributes
279                     Map.Entry entry;
280                     MetaRule rule;
281                     Metadata data;
282                     int ruleEnd = this.rules.size() - 1;
283                     for (Iterator itr = this.attributes.entrySet().iterator();
284                          itr.hasNext(); ) {
285                         entry = (Map.Entry)itr.next();
286                         data = null;
287                         int i = ruleEnd;
288                         while (data == null && i >= 0) {
289                             rule = (MetaRule)this.rules.get(i);
290                             data = rule.applyRule((String)entry.getKey(), (TagAttribute)entry.getValue(), target);
291                             i--;
292                         }
293                         if (data == null) {
294                             if (log.isLoggable(Level.SEVERE)) {
295                                 log.severe(entry.getValue() +
296                                            " Unhandled by MetaTagHandler for type " +
297                                            this.type.getName());
298                             }
299                         } else {
300                             this.mappers.add(data);
301                         }
302                     }
303                 }
304             }
305 
306             if (this.mappers.isEmpty()) {
307                 return NONE;
308             } else {
309                 return new MetadataImpl((Metadata[])this.mappers.toArray(new Metadata[this.mappers.size()]));
310             }
311         }
312 
313         public MetaRuleset ignoreAll() {
314             this.attributes.clear();
315             return this;
316         }
317 
318         private final static Metadata NONE = new Metadata() {
319             public void applyMetadata(FaceletContext ctx, Object instance) {
320                 // do nothing
321             }
322         };
323 
324         final static class MetadataImpl extends Metadata {
325 
326             private final Metadata[] mappers;
327             private final int size;
328 
329             public MetadataImpl(Metadata[] mappers) {
330                 this.mappers = mappers;
331                 this.size = mappers.length;
332             }
333 
334             public void applyMetadata(FaceletContext ctx, Object instance) {
335                 for (int i = 0; i < size; i++) {
336                     this.mappers[i].applyMetadata(ctx, instance);
337                 }
338             }
339 
340         }
341     }
342 
343     private static class MetadataTargetImpl extends MetadataTarget {
344 
345         private final Map pd;
346         private final Class type;
347 
348 
349         public MetadataTargetImpl(Class type) throws IntrospectionException {
350             this.type = type;
351             this.pd = new HashMap();
352             BeanInfo info = Introspector.getBeanInfo(type);
353             PropertyDescriptor[] pda = info.getPropertyDescriptors();
354             for (int i = 0; i < pda.length; i++) {
355                 this.pd.put(pda[i].getName(), pda[i]);
356             }
357         }
358 
359         public PropertyDescriptor getProperty(String name) {
360             return (PropertyDescriptor)this.pd.get(name);
361         }
362 
363         public boolean isTargetInstanceOf(Class type) {
364             return type.isAssignableFrom(this.type);
365         }
366 
367         public Class getTargetClass() {
368             return this.type;
369         }
370 
371         public Class getPropertyType(String name) {
372             PropertyDescriptor pd = this.getProperty(name);
373             if (pd != null) {
374                 return pd.getPropertyType();
375             }
376             return null;
377         }
378 
379         public Method getWriteMethod(String name) {
380             PropertyDescriptor pd = this.getProperty(name);
381             if (pd != null) {
382                 return pd.getWriteMethod();
383             }
384             return null;
385         }
386 
387         public Method getReadMethod(String name) {
388             PropertyDescriptor pd = this.getProperty(name);
389             if (pd != null) {
390                 return pd.getReadMethod();
391             }
392             return null;
393         }
394 
395     }
396 
397 }
398