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: Clay.java 471910 2006-11-06 22:44:56Z gvanmatre $
20   */
21  package org.apache.shale.clay.component;
22  
23  import java.io.IOException;
24  import java.net.URL;
25  import java.util.Iterator;
26  import java.util.Map;
27  import java.util.TreeMap;
28  
29  import javax.faces.component.UIComponent;
30  import javax.faces.component.UIComponentBase;
31  import javax.faces.context.FacesContext;
32  import javax.faces.el.MethodBinding;
33  
34  import org.apache.commons.chain.Catalog;
35  import org.apache.commons.chain.CatalogFactory;
36  import org.apache.commons.chain.Command;
37  import org.apache.commons.chain.config.ConfigParser;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.shale.clay.component.chain.AbstractCommand;
41  import org.apache.shale.clay.component.chain.ClayContext;
42  import org.apache.shale.clay.config.Globals;
43  import org.apache.shale.clay.config.beans.AttributeBean;
44  import org.apache.shale.clay.config.beans.ComponentBean;
45  import org.apache.shale.clay.config.beans.ConfigBean;
46  import org.apache.shale.clay.config.beans.ConfigBeanFactory;
47  import org.apache.shale.clay.config.beans.SymbolBean;
48  import org.apache.shale.util.Messages;
49  
50  /***
51   * This component grafts a subview onto the current JSF view.
52   *
53   */
54  public class Clay extends UIComponentBase {
55  
56      /***
57       * <p>
58       * Commons logger utility class instance.
59       * </p>
60       */
61      private static Log log;
62      static {
63          log = LogFactory.getLog(Clay.class);
64      }
65  
66      /***
67       * <p>
68       * Message resources for this class.
69       * </p>
70       */
71      private static Messages messages = new Messages(
72              "org.apache.shale.clay.Bundle", Clay.class.getClassLoader());
73  
74  
75      /***
76       * <p>Holds the symbol table for the component.</p>
77       */
78      private Map symbols = new TreeMap();
79  
80      /***
81       * <p>Returns the symbol table for the component.</p>
82       *
83       * @return Map of {@link org.apache.shale.clay.config.beans.SymbolBean}
84       */
85      public Map getSymbols() {
86          return symbols;
87      }
88  
89      /***
90       * <p>
91       * Loads the chains config using the resource defined by config file
92       * <code>Globals.CLAY_RESOURCE_NAME</code>. These chain commands are used
93       * to build the clay component subtree from a root {@link ComponentBean}
94       * obtained for a {@link ConfigBean} instance from the
95       * {@link ConfigBeanFactory}.
96       * </p>
97       *
98       * @return {@link Catalog} instance of a catalog defined by catalog
99       *         <code>Globals.CLAY_CATALOG_NAME</code>
100      * @exception Exception loading catalog
101      */
102     protected Catalog getCatalog() throws Exception {
103 
104         // Look up the "shale" catalog, creating one if necessary
105         Catalog catalog = CatalogFactory.getInstance().getCatalog(
106                 Globals.CLAY_CATALOG_NAME);
107         if (catalog == null) {
108 
109             ConfigParser parser = new ConfigParser();
110             URL url = this.getClass().getClassLoader().getResource(
111                     Globals.CLAY_RESOURCE_NAME);
112             if (url == null) {
113                 throw new IllegalArgumentException(Globals.CLAY_RESOURCE_NAME);
114             }
115             parser.parse(url);
116 
117             catalog = CatalogFactory.getInstance().getCatalog(
118                     Globals.CLAY_CATALOG_NAME);
119 
120         }
121 
122         return catalog;
123 
124     }
125 
126     /***
127      * <p>
128      * Unique identifier that is used to define a simple or complex component.
129      * The suffix of this identifier will be used to determine if the component
130      * metadata will be loaded from a HTML style of document or a XML defining
131      * document
132      * </p>
133      */
134     private String jsfid = null;
135 
136 
137     /***
138      * <p>
139      * A <code>validator</code> style of method event that is fired when
140      * building the sub component tree using the <code>jsfid</code> as the key
141      * defining the component metadata.
142      * </p>
143      */
144     private String shapeValidator = null;
145 
146     /***
147      * <p>
148      * The root of the metadata used to build the component subtree.
149      * </p>
150      */
151     private ComponentBean displayElementRoot = null;
152 
153     /***
154      * <p>
155      * Returns the unique identifier used to build the component subtree.
156      * </p>
157      *
158      * @return jsfid
159      */
160     public String getJsfid() {
161         return jsfid;
162     }
163 
164     /***
165      * <p>
166      * Sets the unique identifier used to build the component subtree.
167      * </p>
168      *
169      * @param jsfid root identifier of the sub tree
170      */
171     public void setJsfid(String jsfid) {
172         this.jsfid = jsfid;
173     }
174 
175 
176     /***
177      * <p>
178      * Sets the unique identifier used to build the component subtree.
179      * This property is not accessible from the JSP tag.
180      * </p>
181      *
182      * @param jsfid alias to the jsfid property
183      */
184     public void setClayJsfid(String jsfid) {
185         this.jsfid = jsfid;
186     }
187 
188     /***
189      * <p>
190      * Returns the unique identifier used to build the component subtree.
191      * This property is not accessible from the JSP tag.
192      * </p>
193      *
194      * @return jsfid alias
195      */
196     public String getClayJsfid() {
197        return jsfid;
198     }
199 
200 
201     /***
202      * <p>
203      * Retuns the <code>validator</code> signature event that is invoked when
204      * the component metadata is retrieved.
205      * </p>
206      *
207      * @return method binding expression
208      */
209     public String getShapeValidator() {
210         return shapeValidator;
211     }
212 
213     /***
214      * <p>
215      * Sets the <code>validator</code> signature event that is invoked when
216      * the component metadata is retrieved.
217      * </p>
218      *
219      * @param loadShapeAction method binding expression
220      */
221     public void setShapeValidator(String loadShapeAction) {
222         this.shapeValidator = loadShapeAction;
223     }
224 
225     /***
226      * <p>
227      * Returns the logical bean name that replaces any occurance of
228      * "@managed-bean-name" within a binding expression.
229      * </p>
230      *
231      * @return managed bean name symbol
232      */
233     public String getManagedBeanName() {
234         SymbolBean symbol = (SymbolBean) symbols.get(Globals.MANAGED_BEAN_MNEMONIC);
235         return ((symbol != null) ? symbol.getValue() : null);
236     }
237 
238     /***
239      * <p>
240      * Sets the logical bean name that replaces any occurrences of
241      * "@managed-bean-name" within a binding expression.
242      * </p>
243      *
244      * @param mbeanMnemonic managed bean symbol
245      */
246     public void setManagedBeanName(String mbeanMnemonic) {
247         SymbolBean symbol = new SymbolBean();
248         symbol.setName(Globals.MANAGED_BEAN_MNEMONIC);
249         symbol.setValue(mbeanMnemonic);
250         symbols.put(symbol.getName(), symbol);
251     }
252 
253     /***
254      * <p>
255      * Returns the root metadata component that is used to add to the component
256      * tree. It locates the {@link ComponentBean} using the <code>jsfid</code>
257      * attribute as the key. A call to the {@link ConfigBeanFactory} locates the
258      * correct {@link ConfigBean} used to find the {@link ComponentBean}. </p>
259      *
260      * @return root config bean used to define the subtree
261      */
262     protected ComponentBean getRootElement() {
263 
264         ConfigBean config = ConfigBeanFactory.findConfig(getJsfid());
265 
266         if (config == null) {
267             throw new NullPointerException(messages
268                     .getMessage("clay.config.notloaded"));
269         }
270 
271         // find the top-level display element associated with the subtree
272         ComponentBean b = config.getElement(getJsfid());
273         if (b == null) {
274             throw new NullPointerException(messages.getMessage(
275                     "clay.jsfid.notfound", new Object[] { getJsfid() }));
276         }
277 
278         return b;
279     }
280 
281     /***
282      * <p>
283      * This is where the clay component loads the root {@link ComponentBean} by
284      * calling the
285      * <code>getRootElement()</code> method or while invoking the <code>shapeValidator</code> callback event.
286      * The subtree is created using the <code>Globals.ADD_COMPONENT_COMMAND_NAME</code> from the
287      * <code>Globals.CLAY_CATALOG_NAME</code> in the <code>Globals.CLAY_RESOURCE_NAME</code>.
288      *
289      * @param context faces context
290      * @exception IOException render error
291      */
292     public void encodeBegin(FacesContext context) throws IOException {
293 
294         if (log.isTraceEnabled()) {
295             log.trace("encodeBegin(FacesContext)");
296         }
297 
298         //I added a more descriptive message to the null pointer exception.
299         //The challenge here is that the symbol replacement happens after the
300         //HTML node has been thrown out.  Look to see if the clayJsfid can be
301         //found in the forming ComponentBean.
302         if (getJsfid() == null) {
303             String attrClayJsfid = "null";
304             ComponentBean displayElement = (ComponentBean) getAttributes().get(Globals.CLAY_RESERVED_ATTRIBUTE);
305             AttributeBean attr = displayElement.getAttribute("clayJsfid");
306             if (displayElement != null && (attr != null)) {
307                 attrClayJsfid = attr.getValue();
308             }
309             throw new NullPointerException(messages.getMessage("clay.jsfid.null",
310                   new Object[] { attrClayJsfid}));
311         }
312 
313         if (getDisplayElementRoot() == null) {
314             if (!getJsfid().equals(Globals.RUNTIME_ELEMENT_ID)) {
315                 displayElementRoot = getRootElement();
316             } else {
317                 displayElementRoot = new ComponentBean();
318                 displayElementRoot.setComponentType("javax.faces.HtmlOutputText");
319                 displayElementRoot.setJsfid(getJsfid());
320 
321             }
322 
323             // this callback will give a hook to override
324             // or alter the root display element by
325             // using the getter and setter for the displayElementRoot
326             if (this.getShapeValidator() != null) {
327 
328                 ClayContext clayContext = new ClayContext();
329 
330                 Map symbolTable = new TreeMap();
331                 symbolTable.putAll(getSymbols());
332                 clayContext.setSymbols(symbolTable);
333                 //evaluate nested symbols
334                 AbstractCommand.realizeSymbols(clayContext);
335 
336                 //resolve the literal "@managed-bean-name"
337                 String expr = AbstractCommand.replaceMnemonic(clayContext, getShapeValidator());
338                 Class[] methodSignature = {
339                         javax.faces.context.FacesContext.class,
340                         javax.faces.component.UIComponent.class,
341                         java.lang.Object.class };
342 
343                 MethodBinding binding = getFacesContext().getApplication()
344                     .createMethodBinding(expr, methodSignature);
345 
346                 if (log.isDebugEnabled()) {
347                     log
348                     .debug(messages
349                             .getMessage("clay.invoke.shapeValidator"));
350                 }
351 
352                 Object[] args = { context, this, displayElementRoot };
353                 binding.invoke(context, args);
354 
355             }
356         }
357 
358         // Recursively build the subtree and fixup references. The root
359         // of the subview will be assigned to the child private property.
360 
361         ClayContext clayContext = new ClayContext();
362         clayContext.setChild(null);
363         clayContext.setDisplayElement(getDisplayElementRoot());
364         clayContext.setJsfid(getJsfid());
365         clayContext.setFacesContext(getFacesContext());
366 
367         Map symbolTable = new TreeMap();
368         symbolTable.putAll(getDisplayElementRoot().getSymbols());
369         symbolTable.putAll(getSymbols());
370         clayContext.setSymbols(symbolTable);
371 
372         //evaluate nested symbols
373         AbstractCommand.realizeSymbols(clayContext);
374 
375         clayContext.setRootElement(getDisplayElementRoot());
376         clayContext.setParent(this);
377 
378         Catalog catalog = null;
379         try {
380             catalog = getCatalog();
381         } catch (Exception e) {
382             log.error(e);
383         }
384         Command command = catalog
385                  .getCommand(Globals.ADD_COMPONENT_COMMAND_NAME);
386 
387         try {
388             command.execute(clayContext);
389         } catch (Exception e) {
390             log.error(e);
391             throw new RuntimeException(e);
392         }
393 
394     }
395 
396     /***
397      * <p>
398      * Recursively invokes the rendering of the sub component tree.
399      * </p>
400      *
401      * @param child faces component
402      * @param context faces context
403      * @exception IOException render error
404      */
405     protected void recursiveRenderChildren(UIComponent child,
406             FacesContext context) throws IOException {
407 
408         if (!child.getRendersChildren() || child == this) {
409             Iterator ci = child.getChildren().iterator();
410             while (ci.hasNext()) {
411                 UIComponent c = (UIComponent) ci.next();
412                 if (c.isRendered()) {
413                     c.encodeBegin(context);
414 
415                     if (!c.getRendersChildren()) {
416                         recursiveRenderChildren(c, context);
417                     } else {
418                         c.encodeChildren(context);
419                     }
420 
421                     c.encodeEnd(context);
422                     c = null;
423                 }
424             }
425         } else {
426             // let the component handle iterating over the children
427             child.encodeChildren(context);
428         }
429     }
430 
431     /***
432      * <p>
433      * Called by JSF, this method delegates to <code>recursiveRenderChildren</code>.
434      * </p>
435      *
436      * @param context faces context
437      * @exception IOException render error
438      */
439     public void encodeChildren(FacesContext context) throws IOException {
440 
441         if (log.isTraceEnabled()) {
442             log.trace("encodeChildren(FacesContext)");
443         }
444 
445         recursiveRenderChildren(this, context);
446 
447     }
448 
449     /***
450      * <p>
451      * Called by JSF, this method simply emits a logging statement
452      * if tracing is enabled.
453      * </p>
454      *
455      * @param context faces context
456      * @exception IOException render error
457      */
458     public void encodeEnd(FacesContext context) throws IOException {
459 
460         if (log.isTraceEnabled()) {
461             log.trace("encodeEnd(FacesContext)");
462         }
463 
464     }
465 
466     /***
467      * <p>
468      * Returns the root {@link ComponentBean} used to build the clay subtree
469      * component.
470      * </p>
471      *
472      * @return root config bean
473      */
474     protected ComponentBean getDisplayElementRoot() {
475         return displayElementRoot;
476     }
477 
478     /***
479      * <p>
480      * Sets the root {@link ComponentBean} used to build the clay subtree
481      * component.
482      * </p>
483      *
484      * @param displayElementRoot root config bean
485      */
486     protected void setDisplayElementRoot(ComponentBean displayElementRoot) {
487         this.displayElementRoot = displayElementRoot;
488     }
489 
490     /***
491      * <p>
492      * Restores a component's state.
493      * </p>
494      *
495      * @param context faces context
496      * @param obj component state
497      */
498     public void restoreState(FacesContext context, Object obj) {
499 
500         final int superState = 0;
501         final int jsfidState = 1;
502         final int shapeValidatorState = 2;
503         final int displayElementState = 3;
504         final int symbolsState = 4;
505 
506         Object[] aobj = (Object[]) obj;
507         super.restoreState(context, aobj[superState]);
508 
509         jsfid = ((String) aobj[jsfidState]);
510         shapeValidator = ((String) aobj[shapeValidatorState]);
511         displayElementRoot = ((ComponentBean) aobj[displayElementState]);
512         symbols = ((Map) aobj[symbolsState]);
513 
514     }
515 
516     /***
517      * <p>
518      * Saves a component's state.
519      * </p>
520      *
521      * @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
522      * @param context faces context
523      * @return subtree state
524      */
525     public Object saveState(FacesContext context) {
526         final int superState = 0;
527         final int jsfidState = 1;
528         final int shapeValidatorState = 2;
529         final int displayElementState = 3;
530         final int symbolsState = 4;
531         final int size = 5;
532 
533         Object[] aobj = new Object[size];
534         aobj[superState] = super.saveState(context);
535         aobj[jsfidState] = jsfid;
536         aobj[shapeValidatorState] = shapeValidator;
537         aobj[displayElementState] = displayElementRoot;
538         aobj[symbolsState] = symbols;
539 
540         return aobj;
541     }
542 
543     /***
544      * <p>Returns <code>true</code> indicating that this
545      * component renders it's children. That means JSF will
546      * invoke <code>encodeChildren()</code> method.</p>
547      *
548      * @return <code>true</code>
549      */
550     public boolean getRendersChildren() {
551         return true;
552     }
553 
554     /***
555      * @return Returns the component's family.
556      */
557     public String getFamily() {
558         return "org.apache.shale.clay";
559     }
560 
561 
562 }