2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * $Id: JsfDefaultBuilder.java 471910 2006-11-06 22:44:56Z gvanmatre $
20   */
21  package org.apache.shale.clay.parser.builder;
22  
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.TreeMap;
28  
29  import org.apache.shale.clay.config.beans.ActionListenerBean;
30  import org.apache.shale.clay.config.beans.AttributeBean;
31  import org.apache.shale.clay.config.beans.ComponentBean;
32  import org.apache.shale.clay.config.beans.ConverterBean;
33  import org.apache.shale.clay.config.beans.ElementBean;
34  import org.apache.shale.clay.config.beans.SymbolBean;
35  import org.apache.shale.clay.config.beans.ValidatorBean;
36  import org.apache.shale.clay.config.beans.ValueChangeListenerBean;
37  import org.apache.shale.clay.parser.Node;
38  import org.apache.shale.util.Tags;
39  
40  /***
41   * <p>A generic builder that maps the markup node name to the jsfid.  The extends attribute
42   * can be used to override the default mapping to provide meta-data inheritance.  The builder
43   * handles child nodes common to JSF and shale components.</p>
44   *
45   */
46  public class JsfDefaultBuilder extends ElementBuilder {
47  
48      /***
49       * <p>Utility class that helps evaluate binding expressions.</p>
50       */
51      private Tags tagsUtil = new Tags();
52  
53      /***
54       * <p>Contains the namespace prefix that should be used to locate
55       * clay configurations.  This allows the page to use any prefix
56       * but still find the config element.</p>
57       */
58      private String prefix = null;
59  
60      /***
61       * <p>Returns the namespace prefix that will be added to the
62       * node name when resolving the clay config.</p>
63       *
64       * @return URI prefix
65       */
66      public String getPrefix() {
67          return prefix;
68      }
69  
70      /***
71       * <p>Sets the namespace preix that will override the template
72       * nodeds qname.</p>
73       *
74       * @param prefix URI prefix
75       */
76      public void setPrefix(String prefix) {
77          this.prefix = prefix;
78      }
79  
80  
81      /***
82       * <p>Factory method that creates a {@link ElementBean} from a {@link Node}.</p>
83       *
84       * @param node markup
85       * @return new config bean from the node
86       */
87      public ElementBean createElement(Node node) {
88          ElementBean target = new ElementBean();
89          target.setJsfid(getJsfid(node));
90          target.setRenderId(getRenderId());
91  
92          return target;
93      }
94  
95      /***
96       * <p>Holds a cross reference of commonsValidator type to
97       * a clay component config definition.</p>
98       */
99      private Map validatorsByType = null;
100     {
101       validatorsByType = new TreeMap();
102       validatorsByType.put("required", "s:commonsValidatorRequired");
103       validatorsByType.put("maxlength", "s:commonsValidatorMaxlength");
104       validatorsByType.put("minlength", "s:commonsValidatorMinlength");
105       validatorsByType.put("mask", "s:commonsValidatorMask");
106       validatorsByType.put("byte", "s:commonsValidatorByte");
107       validatorsByType.put("short", "s:commonsValidatorShort");
108       validatorsByType.put("integer", "s:commonsValidatorInteger");
109       validatorsByType.put("long", "s:commonsValidatorLong");
110       validatorsByType.put("float", "s:commonsValidatorFloat");
111       validatorsByType.put("double", "s:commonsValidatorDouble");
112       validatorsByType.put("date", "s:commonsValidatorDate");
113       validatorsByType.put("intRange", "s:commonsValidatorIntRange");
114       validatorsByType.put("floatRange", "s:commonsValidatorFloatRange");
115       validatorsByType.put("doubleRange", "s:commonsValidatorDoubleRange");
116       validatorsByType.put("creditCard", "s:commonsValidatorCreditCard");
117       validatorsByType.put("email", "s:commonsValidatorEmail");
118       validatorsByType.put("url", "s:commonsValidatorUrl");
119     }
120 
121     /***
122      * @inheritDoc
123      * @param node markup
124      * @param target child config bean
125      */
126     protected void addConverter(Node node, ElementBean target) {
127         ConverterBean targetConverter = new ConverterBean();
128 
129         //make sure we have the correct jsfid based on the namespace prefix
130         Builder tmpBuilder = getBuilder(node);
131         ElementBean tmp = tmpBuilder.createElement(node);
132 
133         String jsfid = tmp.getJsfid();
134         targetConverter.setJsfid(jsfid);
135 
136         String converterId = (String) node.getAttributes().get("converterId");
137         if (converterId != null) {
138             targetConverter.setComponentType(tagsUtil.evalString(converterId));
139         }
140 
141         // resolve inheritance and attribute overrides
142         if (node.getAttributes().containsKey("extends") || !jsfid.equals("converter")) {
143             realizeComponent(node, targetConverter);
144         }
145 
146         //attach to the target element
147         target.addConverter(targetConverter);
148 
149     }
150 
151     /***
152      * <p>Looks for &lt;s:validatorVar/&gt; nodes within a &lt;s:commonsValidator&gt; node and
153      * converting them to {@link org.apache.shale.clay.config.beans.AttributeBean}'s
154      * on the <code>target</code> {@link org.apache.shale.clay.config.beans.ComponentBean}.
155      * </p>
156      *
157      * @param attributesNode markup
158      * @param target child config bean
159      */
160     protected void addValidatorVar(Node attributesNode, ComponentBean target) {
161         Iterator ci = attributesNode.getChildren().iterator();
162         while (ci.hasNext()) {
163             Node child = (Node) ci.next();
164             if (child.isWellFormed() && child.getName() != null
165                 && child.getName().equals("validatorVar")) {
166 
167                 String name = (String) child.getAttributes().get("name");
168                 String value = (String) child.getAttributes().get("value");
169                 String bindingType = (String) child.getAttributes().get("bindingType");
170 
171                 AttributeBean attr = target.getAttribute(name);
172                 if (attr != null) {
173                     createAttribute(attr, value, target);
174                 } else {
175                     attr = new AttributeBean();
176                     attr.setName(name);
177                     attr.setValue(value);
178                     attr.setBindingType(bindingType);
179                     target.addAttribute(attr);
180                 }
181             }
182         }
183     }
184 
185     /***
186      * @inheritDoc
187      * @param node markup
188      * @param target child config bean
189      */
190     protected void addValidator(Node node, ElementBean target) {
191         ValidatorBean targetValidator = new ValidatorBean();
192 
193         //make sure we have the correct jsfid based on the namespace prefix
194         Builder tmpBuilder = getBuilder(node);
195         ElementBean tmp = tmpBuilder.createElement(node);
196 
197         String jsfid = tmp.getJsfid();
198         targetValidator.setJsfid(jsfid);
199 
200         String validatorId = (String) node.getAttributes().get("validatorId");
201         if (validatorId != null) {
202             targetValidator.setComponentType(tagsUtil.evalString(validatorId));
203         }
204 
205         // resolve inheritance and attribute overrides
206         if (node.getAttributes().containsKey("extends") || !jsfid.equals("validator")) {
207             realizeComponent(node, targetValidator);
208         }
209 
210         //attach to the target element
211         target.addValidator(targetValidator);
212 
213         if (node.getName().equals("commonsValidator")) {
214             //You can use multiple commonsValidators per component.  The
215             //jsfid is the key in the ComponentBean's validator set.
216             //Override with the unique type attribute
217             String type = (String) node.getAttributes().get("type");
218             targetValidator.setJsfid((String) validatorsByType.get(type));
219 
220             //add support for the validatorVar JSP tag.
221             addValidatorVar(node, targetValidator);
222         }
223 
224     }
225 
226     /***
227      * @inheritDoc
228      * @param node markup
229      * @param target child config bean
230      */
231     protected void addActionListener(Node node, ElementBean target) {
232         ActionListenerBean targetActionListener = new ActionListenerBean();
233 
234         //make sure we have the correct jsfid based on the namespace prefix
235         Builder tmpBuilder = getBuilder(node);
236         ElementBean tmp = tmpBuilder.createElement(node);
237 
238         String jsfid = tmp.getJsfid();
239         targetActionListener.setJsfid(jsfid);
240 
241         String type = (String) node.getAttributes().get("type");
242         if (type != null) {
243             targetActionListener.setComponentType(tagsUtil.evalString(type));
244         }
245 
246         // resolve inheritance and attribute overrides
247         if (node.getAttributes().containsKey("extends") || !jsfid.equals("actionListener")) {
248             realizeComponent(node, targetActionListener);
249         }
250         //attach to the target element
251         target.addActionListener(targetActionListener);
252 
253     }
254 
255     /***
256      * @inheritDoc
257      * @param node markup
258      * @param target child config bean
259      */
260     protected void addValueChangeListener(Node node, ElementBean target) {
261         ValueChangeListenerBean targetValueChangeListener = new ValueChangeListenerBean();
262 
263         //make sure we have the correct jsfid based on the namespace prefix
264         Builder tmpBuilder = getBuilder(node);
265         ElementBean tmp = tmpBuilder.createElement(node);
266 
267         String jsfid = tmp.getJsfid();
268         targetValueChangeListener.setJsfid(jsfid);
269 
270         String type = (String) node.getAttributes().get("type");
271         if (type != null) {
272             targetValueChangeListener.setComponentType(tagsUtil.evalString(type));
273         }
274 
275         // resolve inheritance and attribute overrides
276         if (node.getAttributes().containsKey("extends") || !jsfid.equals("valueChangeListener")) {
277             realizeComponent(node, targetValueChangeListener);
278         }
279 
280         //attach to the target element
281         target.addValueChangeListener(targetValueChangeListener);
282     }
283 
284     /***
285      * <p>Adds markup &lt;clay:symbol&gt; to the <code>target</code>
286      * {@link org.apache.shale.clay.config.beans.ElementBean}.
287      * </p>
288      *
289      * @param node markup
290      * @param target child config bean
291      */
292     protected void addSymbol(Node node, ElementBean target) {
293         String value = (String) node.getAttributes().get("value");
294         String name = (String) node.getAttributes().get("name");
295         if (name != null && name.length() > 0) {
296             SymbolBean symbol = new SymbolBean();
297             StringBuffer tmp = new StringBuffer(name);
298             if (tmp.charAt(0) != '@') {
299                tmp.insert(0, '@');
300             }
301 
302             symbol.setName(tmp.toString());
303             symbol.setValue(value);
304             target.addSymbol(symbol);
305         }
306     }
307 
308     /***
309      * <p>Adds markup &lt;f:attribute&gt; to the <code>target</code>
310      * {@link org.apache.shale.clay.config.beans.ElementBean}.
311      * </p>
312      *
313      * @param node markup
314      * @param target child config bean
315      */
316     protected void addAttribute(Node node, ElementBean target) {
317         String name = (String) target.getAttributes().get("name");
318         String value = (String) target.getAttributes().get("value");
319 
320         AttributeBean attr = target.getAttribute(name);
321         if (attr != null) {
322             createAttribute(attr, value, target);
323         } else {
324             attr = new AttributeBean();
325             attr.setName(name);
326             attr.setValue(value);
327             attr.setBindingType(AttributeBean.BINDING_TYPE_EARLY);
328             target.addAttribute(attr);
329         }
330     }
331 
332     /***
333      * <p>Adds markup &lt;f:facet&gt; to the <code>target</code>'s
334      * child {@link org.apache.shale.clay.config.beans.ElementBean}.
335      * </p>
336      *
337      * @param node markup
338      * @param target child config bean
339      */
340     protected void addFacet(Node node, ElementBean target) {
341         String facetName = (String) node.getAttributes().get("name");
342         Iterator ci = node.getChildren().iterator();
343         // look for the first well-formed node.  only one under
344         // a facet.  Call back on the current builder
345         while (ci.hasNext()) {
346             Node child = (Node) ci.next();
347             if (child.isWellFormed()) {
348                 Builder childBuilder = getBuilder(child);
349                 ElementBean nextTarget = childBuilder.createElement(child);
350                 nextTarget.setFacetName(facetName);
351                 target.addChild(nextTarget);
352                 childBuilder.encode(child, nextTarget, nextTarget);
353                 break;
354             }
355         }
356     }
357 
358 
359     /***
360      * <p>Build's a <code>target</code> {@link ElementBean} from a {@link Node}. The
361      * following child nodes are handles outside of the <code>encodeChildren</code>
362      * method: symbol, facet, attribute, convert, validate, actionListener,
363      * and valueChangeListener.
364      * </p>
365      *
366      * @param node markup
367      * @param target child config bean
368      * @param root parent config bean
369      */
370     protected void encodeBegin(Node node, ElementBean target, ComponentBean root) {
371         assignNode(node, target);
372 
373         List deleteList = new ArrayList();
374         Iterator ci = node.getChildren().iterator();
375         next: while (ci.hasNext()) {
376             Node child = (Node) ci.next();
377             if (child.isWellFormed() && child.getName() != null) {
378                 if (child.getName().equals("symbol")) {
379                     addSymbol(child, target);
380                     deleteList.add(child);
381                 } else if (child.getName().equals("facet")) {
382                     addFacet(child, target);
383                     deleteList.add(child);
384                 } else if (child.getName().equals("attribute")) {
385                     addAttribute(child, target);
386                     deleteList.add(child);
387                 } else if (child.getName().startsWith("convert")) {
388                     addConverter(child, target);
389                     deleteList.add(child);
390                 } else if (child.getName().startsWith("validate")
391                         || child.getName().startsWith("validator")
392                         || child.getName().startsWith("commonsValidator")) {
393                     addValidator(child, target);
394                     deleteList.add(child);
395                 } else if (child.getName().equals("actionListener")) {
396                     addActionListener(child, target);
397                     deleteList.add(child);
398                 } else if (child.getName().equals("valueChangeListener")) {
399                     addValueChangeListener(child, target);
400                     deleteList.add(child);
401                 }
402             } else {
403                 if (node.getName() != null && node.getName().equals("verbatim")) {
404                     continue next;
405                 }
406                 if (child.isComment() || isNodeWhitespace(child)) {
407                     // remove white space
408                     deleteList.add(child);
409                 }
410             }
411         }
412 
413         ci = deleteList.iterator();
414         while (ci.hasNext()) {
415             node.getChildren().remove(ci.next());
416         }
417 
418     }
419 
420     /***
421      * <p>Returns the <code>jsfid</code> from the {@link Node} The <code>extends</code>
422      * attribute is giving the first order of evaluation.  If empty, the <code>node</code>'s
423      * name is assigned to the jsfid.</p>
424      *
425      * @param node markup
426      * @return jsfid
427      */
428     protected String getJsfid(Node node) {
429         StringBuffer jsfid = new StringBuffer();
430         if (node.getAttributes().containsKey("extends")) {
431             jsfid.append(node.getAttributes().get("extends"));
432         } else {
433             jsfid.append(node.getName());
434 
435             //override the node prefix with the one
436             //assigned to the uri.  the prefix will
437             //match the config beans
438 
439             String prefix = getPrefix();
440             if (prefix == null) {
441                 prefix = node.getQname();
442             }
443             if (prefix != null) {
444                 jsfid.insert(0, ':');
445                 jsfid.insert(0, prefix);
446             }
447         }
448 
449 
450         return jsfid.toString();
451     }
452 
453     /***
454      * <p>Returns the {@link org.apache.shale.clay.parser.builder.Builder} that
455      * is assigned the task of converting the html node to a corresponding component
456      * metadata used to construct a JSF resource.</p>
457      *
458      * @param node markup node
459      * @return builder that maps markup to config beans
460      */
461     public Builder getBuilder(Node node) {
462         return BuilderFactory.getRenderer(node);
463     }
464 
465 }