2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
299
300
301
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
324
325
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
334 AbstractCommand.realizeSymbols(clayContext);
335
336
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
359
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
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
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 }