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: ElementBuilder.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  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
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.ConfigBean;
33  import org.apache.shale.clay.config.beans.ConfigBeanFactory;
34  import org.apache.shale.clay.config.beans.ConverterBean;
35  import org.apache.shale.clay.config.beans.ElementBean;
36  import org.apache.shale.clay.config.beans.SymbolBean;
37  import org.apache.shale.clay.config.beans.ValidatorBean;
38  import org.apache.shale.clay.config.beans.ValueChangeListenerBean;
39  import org.apache.shale.clay.parser.Node;
40  
41  /***
42   * <p>This class handles building the {@link org.apache.shale.clay.config.beans.ElementBean}'s
43   * from the html markup resembling the &lt;attributes&gt; node in the clay
44   * DTD, http://shale.apache.org/dtds/clay-config_1_0.dtd.</p>
45   */
46  public class ElementBuilder extends Builder {
47  
48      /***
49       * <p>Common Logger utility class.</p>
50       */
51      private static Log log;
52      static {
53          log = LogFactory.getLog(ElementBuilder.class);
54      }
55  
56  
57      /***
58       * <p>Returns the <code>jsfid</code> from the target HTML
59       * {@link org.apache.shale.clay.parser.Node}.</p>
60       *
61       * @param node markup
62       * @return jsfid
63       */
64      protected String getJsfid(Node node) {
65          String jsfid = (String) node.getAttributes().get("jsfid");
66          return jsfid;
67      }
68  
69      /***
70       * <p>Returns the <code>componentType</code> from the target HTML
71       * {@link org.apache.shale.clay.parser.Node}.</p>
72       *
73       * @param node markup
74       * @return component type
75       */
76      protected String getComponentType(Node node) {
77          String componentType = (String) node.getAttributes().get("componentType");
78          return componentType;
79      }
80  
81      /***
82       * <p>Adds a {@link org.apache.shale.clay.config.beans.ConverterBean}
83       * to the <code>target</code> {@link org.apache.shale.clay.config.beans.ElementBean}
84       * using the {@link org.apache.shale.clay.parser.Node} as the input source.</p>
85       *
86       * @param node markup
87       * @param target child config bean
88       */
89      protected void addConverter(Node node, ElementBean target) {
90         ConverterBean targetConverter = new ConverterBean();
91  
92         String jsfid = getJsfid(node);
93         targetConverter.setJsfid(jsfid);
94  
95         // resolve inheritance and attribute overrides
96         realizeComponent(node, targetConverter);
97         //attach to the target element
98         target.addConverter(targetConverter);
99  
100     }
101 
102 
103     /***
104      * <p>Adds a {@link org.apache.shale.clay.config.beans.ValidatorBean}
105      * to the <code>target</code> {@link org.apache.shale.clay.config.beans.ElementBean}
106      * using the {@link org.apache.shale.clay.parser.Node} as the input source.</p>
107      *
108      * @param node markup
109      * @param target child config bean
110      */
111     protected void addValidator(Node node, ElementBean target) {
112        ValidatorBean targetValidator = new ValidatorBean();
113 
114        String jsfid = getJsfid(node);
115        targetValidator.setJsfid(jsfid);
116 
117        // resolve inheritance and attribute overrides
118        realizeComponent(node, targetValidator);
119        //attach to the target element
120        target.addValidator(targetValidator);
121 
122     }
123 
124     /***
125      * <p>Adds an {@link org.apache.shale.clay.config.beans.ActionListenerBean}
126      * to the <code>target</code> {@link org.apache.shale.clay.config.beans.ElementBean}
127      * using the {@link org.apache.shale.clay.parser.Node} as the input source.</p>
128      *
129      * @param node markup
130      * @param target child config bean
131      */
132     protected void addActionListener(Node node, ElementBean target) {
133        ActionListenerBean targetActionListener = new ActionListenerBean();
134 
135        String jsfid = getJsfid(node);
136        targetActionListener.setJsfid(jsfid);
137 
138        // resolve inheritance and attribute overrides
139        realizeComponent(node, targetActionListener);
140        //attach to the target element
141        target.addActionListener(targetActionListener);
142 
143     }
144 
145     /***
146      * <p>Adds a {@link org.apache.shale.clay.config.beans.ActionListenerBean}
147      * to the <code>target</code> {@link org.apache.shale.clay.config.beans.ElementBean}
148      * using the {@link org.apache.shale.clay.parser.Node} as the input source.</p>
149      *
150      * @param node markup
151      * @param target child config bean
152      */
153     protected void addValueChangeListener(Node node, ElementBean target) {
154        ValueChangeListenerBean targetValueChangeListener = new ValueChangeListenerBean();
155 
156        String jsfid = getJsfid(node);
157        targetValueChangeListener.setJsfid(jsfid);
158 
159        // resolve inheritance and attribute overrides
160        realizeComponent(node, targetValueChangeListener);
161        //attach to the target element
162        target.addValueChangeListener(targetValueChangeListener);
163     }
164 
165 
166     /***
167      * <p>Realizes the inheritance of the <code>target</code>
168      * {@link org.apache.shale.clay.config.beans.ComponentBean} and
169      * and then applies attributes that are optionally nested
170      * under the <code>node</code>.</p>
171      *
172      * @param node markup
173      * @param target child config bean
174      */
175     protected void realizeComponent(Node node, ComponentBean target) {
176         // lookup the ConfigBean that handles the id
177         ConfigBean config = ConfigBeanFactory.findConfig(target.getJsfid());
178 
179         try {
180            //assign the parent
181            config.assignParent(target);
182            // resolve inheritance
183            config.realizingInheritance(target);
184         } catch (RuntimeException e) {
185             log.error(e);
186             throw new RuntimeException(
187                     messages.getMessage("parser.unresolved",
188                     new Object[] {node.getToken(), node.getToken().getRawText()}));
189         }
190 
191         assignAttributes(node, target);
192 
193         //look for attributes
194         Iterator ci = node.getChildren().iterator();
195         while (ci.hasNext()) {
196            Node child = (Node) ci.next();
197            if (child.isWellFormed() && child.getName() != null
198                && child.getName().equals("attributes")) {
199 
200                    addAttributes(child, target);
201            }
202         }
203 
204     }
205 
206 
207     /***
208      * <p>Looks for &lt;set/&gt; nodes within a &lt;attributes&gt; node and
209      * converting them to {@link org.apache.shale.clay.config.beans.AttributeBean}'s
210      * on the <code>target</code> {@link org.apache.shale.clay.config.beans.ComponentBean}.
211      * </p>
212      *
213      * @param attributesNode markup
214      * @param target child config bean
215      */
216     protected void addAttributes(Node attributesNode, ComponentBean target) {
217         Iterator ci = attributesNode.getChildren().iterator();
218         while (ci.hasNext()) {
219             Node child = (Node) ci.next();
220             if (child.isWellFormed() && child.getName() != null
221                 && child.getName().equals("set")) {
222 
223                 String name = (String) child.getAttributes().get("name");
224                 String value = (String) child.getAttributes().get("value");
225                 String bindingType = (String) child.getAttributes().get("bindingType");
226 
227                 AttributeBean attr = target.getAttribute(name);
228                 if (attr != null) {
229                     createAttribute(attr, value, target);
230                 } else {
231                     attr = new AttributeBean();
232                     attr.setName(name);
233                     attr.setValue(value);
234                     attr.setBindingType(bindingType);
235                     target.addAttribute(attr);
236                 }
237             }
238         }
239     }
240 
241 
242     /***
243      * <p>Adds markup <code>symbols</code> to the <code>target</code>
244      * {@link org.apache.shale.clay.config.beans.ElementBean}.
245      * </p>
246      *
247      * @param symbolsNode markup
248      * @param target child config bean
249      */
250     protected void addSymbols(Node symbolsNode, ElementBean target) {
251         Iterator si = symbolsNode.getChildren().iterator();
252         while (si.hasNext()) {
253             Node child = (Node) si.next();
254             if (child.isWellFormed() && child.getName() != null
255                 && child.getName().equals("set")) {
256 
257                 String name = (String) child.getAttributes().get("name");
258                 String value = (String) child.getAttributes().get("value");
259 
260                 if (name != null && name.length() > 0) {
261                     SymbolBean symbol = new SymbolBean();
262                     StringBuffer tmp = new StringBuffer(name);
263                     if (tmp.charAt(0) != '@') {
264                        tmp.insert(0, '@');
265                     }
266 
267                     symbol.setName(tmp.toString());
268                     symbol.setValue(value);
269                     target.addSymbol(symbol);
270                 }
271             }
272         }
273     }
274 
275 
276 
277     /***
278      * <p>Handles converting markup resembling the &lt;element&gt; node
279      * in the clay DTD, http://shale.apache.org/dtds/clay-config_1_0.dtd,
280      * to the target {@link org.apache.shale.clay.config.beans.ElementBean}.</p>
281      *
282      * @param node markup
283      * @param target child config bean
284      * @param root parent config bean
285      */
286     protected void encodeBegin(Node node, ElementBean target, ComponentBean root) {
287         super.encodeBegin(node, target, root);
288 
289         List deleteList = new ArrayList();
290         Iterator ci = node.getChildren().iterator();
291         while (ci.hasNext()) {
292             Node child = (Node) ci.next();
293             if (child.isWellFormed() && child.getName() != null) {
294                 if (child.getName().equals("attributes")) {
295                     addAttributes(child, target);
296                     deleteList.add(child);
297                 } else if (child.getName().equals("symbols")) {
298                     addSymbols(child, target);
299                     deleteList.add(child);
300                 } else if (child.getName().equals("converter")) {
301                     addConverter(child, target);
302                     deleteList.add(child);
303                 } else if (child.getName().equals("validator")) {
304                     addValidator(child, target);
305                     deleteList.add(child);
306                 } else if (child.getName().equals("actionListener")) {
307                     addActionListener(child, target);
308                     deleteList.add(child);
309                 } else if (child.getName().equals("valueChangeListener")) {
310                     addValueChangeListener(child, target);
311                     deleteList.add(child);
312                 }
313             } else {
314                 if (child.isComment() || isNodeWhitespace(child)) {
315                     // remove white space
316                     deleteList.add(child);
317                 }
318             }
319         }
320 
321         ci = deleteList.iterator();
322         while (ci.hasNext()) {
323             node.getChildren().remove(ci.next());
324         }
325 
326     }
327 
328 
329     /***
330      * <p>This method is overridden to look for a <code>renderId</code>
331      * attribute in the {@link org.apache.shale.clay.parser.Node}.
332      * If one exists, it is applied to the target
333      * {@link org.apache.shale.clay.config.beans.ElementBean}. The
334      * super class {@link Builder} generates a unique id by default.
335      * The clay namespace HTML nodes can override the renderId to
336      * allow overridding of nested elements.</p>
337      *
338      * @param node markup
339      * @return config bean
340      */
341     public ElementBean createElement(Node node) {
342         ElementBean target = super.createElement(node);
343         String renderId = (String) node.getAttributes().get("renderId");
344         if (renderId != null) {
345            Integer id = null;
346            try {
347             id = Integer.valueOf(renderId);
348            } catch (NumberFormatException e) {
349                log.error(e);
350                throw new RuntimeException(
351                        messages.getMessage("parser.unresolved",
352                        new Object[] {node.getToken(), node.getToken().getRawText()}));
353            }
354            if (id != null) {
355               target.setRenderId(id.intValue());
356            }
357         }
358 
359         return target;
360     }
361 
362     /***
363      * <p>
364      * This override returns <code>true</code> indicating that the from JSF
365      * component can have children.
366      * </p>
367      *
368      * @return <code>true</code>
369      */
370     public boolean isChildrenAllowed() {
371         return true;
372     }
373 
374 
375     /***
376      * <p>
377      * This method resolves the <code>jsfid</code> attribute for an HTML
378      * element to a component definition in the XML configuration files.
379      * </p>
380      *
381      * @param node markup
382      * @param target child config bean
383      */
384     protected void assignNode(Node node, ElementBean target) {
385         //the name attribute can not be used here because of the conflict with
386         //the param tag.
387         String id = (String) node.getAttributes().get("id");
388         target.setId(id);
389 
390         // look to see if this node should be bound to a component
391         if (target.getJsfid() != null) {
392             // lookup the ConfigBean that handles the id
393             ConfigBean config = ConfigBeanFactory.findConfig(target.getJsfid());
394             // disconnect component type
395             target.setComponentType(null);
396 
397 
398             try {
399                //assign the parent
400                config.assignParent(target);
401                // resolve inheritance
402                config.realizingInheritance(target);
403             } catch (RuntimeException e) {
404                 log.error(e);
405                 throw new RuntimeException(
406                         messages.getMessage("parser.unresolved",
407                         new Object[] {node.getToken(), node.getToken().getRawText()}));
408             }
409 
410             // if the inheritance is broken, toggle back to the default
411             if (target.getComponentType() == null) {
412                 target.setComponentType(this.getComponentType(node));
413             }
414 
415         }
416 
417         // HTML attributes will override the declarative component
418         assignAttributes(node, target);
419 
420     }
421 
422 
423     /***
424      * <p>Test the value of the node and returns <code>true</code> if
425      * the value is only whitespace.</p>
426      *
427      * @param node markup node
428      * @return <code>true</code> if value of the node is only whitespace
429      */
430     protected boolean isNodeWhitespace(Node node) {
431         StringBuffer document = node.getToken().getDocument();
432         for (int i = node.getToken().getBeginOffset();
433              i < node.getToken().getEndOffset(); i++) {
434            char c = document.charAt(i);
435            if (!Character.isWhitespace(c)) {
436                return false;
437            }
438         }
439         return true;
440     }
441 
442 }