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