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.config.beans;
22
23 import java.io.Serializable;
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.TreeSet;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.shale.util.Messages;
32
33 /***
34 * <p>This is the base class of most of the metadata that is used by the
35 * {@link org.apache.shale.clay.component.Clay} component to build
36 * a component subtree.
37 *
38 * <dl>
39 * <dt> There are three sources that populate this object:
40 * <dd> {@link org.apache.shale.clay.config.ClayXmlParser} - from XML config files
41 * <dd> {@link org.apache.shale.clay.parser.builder.Builder} - extending classes
42 * <dd> {@link org.apache.shale.clay.component.Clay} - <code>shapeValidator</code>
43 * a <code>validator</code> style of event method binding
44 * </dl>
45 * </p>
46 */
47 public class ComponentBean extends AbstractBean implements Comparable, Serializable {
48
49 /***
50 * <p>Unique id used by the Serializable interface.</p>
51 */
52 private static final long serialVersionUID = 3907217039524312373L;
53
54 /***
55 * <p>Common Logging utility class.</p>
56 */
57 private static Log log;
58 static {
59 log = LogFactory.getLog(ComponentBean.class);
60 }
61
62 /***
63 * <p>A class scoped unique sequence counter.</p>
64 */
65 private static long uniqueSequence = -1;
66
67 /***
68 * @return the next <code>uniqueSequence</code>
69 */
70 private synchronized long generateId() {
71 return ++uniqueSequence;
72 }
73
74 /***
75 * <p>The config beans unique sequence. The value is
76 * populated from a call to <code>generateId</code>.</p>
77 */
78 private long jspId = -1;
79
80 /***
81 * <p>Returns a unique id that will stick to the config bean.
82 * This is clay's version of the <code>JspIdConsumer</code>
83 * in JSP 2.1.</p>
84 *
85 * @return unique id for a view element
86 */
87 public String getJspId() {
88 if (jspId == -1) {
89 jspId = generateId();
90 }
91 return Long.toString(jspId);
92 }
93
94 /***
95 * <p>
96 * Message resources for this class.
97 * </p>
98 */
99 private static Messages messages = new Messages(
100 "org.apache.shale.clay.Bundle", ComponentBean.class
101 .getClassLoader());
102
103 /***
104 * <p>Unique id that points to component meta information.</p>
105 */
106 private String jsfid = null;
107
108 /***
109 * <p>This value pair collection is used to set the property values for JSF object
110 * implementing <code>UIComponent, Validator, ValueChangeListener, ActionListener
111 * and Converter</code>.
112 * </p>
113 */
114 private Map attributes = new Attributes();
115
116 /***
117 * <p>An object reference that shows a generalization relationship through the
118 * metadata. The <code>extends</code> attribute will hold the <code>jsfid</code>
119 * of the parent this instance extends.
120 * </p>
121 */
122 private ComponentBean isAParent = null;
123
124 /***
125 * <p>An object reference that shows a composition relationship. This reference
126 * will point to the parent that holds this object instance in one of it's collections.
127 *</p>
128 */
129 private ComponentBean hasAParent = null;
130
131 /***
132 * <p>The <code>componentType</code> relates to a JSF component type used use to instantiate
133 * the component using abstract factories. For component's like <code>ActionListener and
134 * ValueChangeListener</code>, that are not registered in the faces configuration file, the
135 * <code>componentType</code> is the fully qualified class name.
136 * </p>
137 */
138 private String componentType = null;
139
140 /***
141 * <p>The <code>jsfid</code> of the meta component parent.</p>
142 */
143 private String extendsElementId = null;
144
145 /***
146 * <p>Child meta components that form composition under another meta
147 * component instance. Each instance in this set will be a instance
148 * of {@link ElementBean} and uniquely identified by <code>renderId</code>
149 * </p>
150 */
151 private Collection children = new TreeSet();
152
153 /***
154 * <p>Boolean flag indicates the meta inheritance of this component
155 * has been resolved.</p>
156 */
157 private boolean isInheritanceFinal = false;
158
159 /***
160 * <p>Reference to an associated {@link ComponentBean} that is an instance of
161 * {@link ConverterBean} and is used to instantiate a JSF <code>Converter</code>.
162 * </p>
163 */
164 private ComponentBean converter = null;
165
166 /***
167 * <p>Reference to a set of associated {@link ComponentBean} that is an instance of
168 * {@link ValidatorBean} and is used to instantiate a JSF <code>Validator</code>.
169 * </p>
170 */
171 private TreeSet validators = new TreeSet();
172
173 /***
174 * <p>Reference to a set of associated {@link ComponentBean} that is an instance of
175 * {@link ValueChangeListenerBean} and is used to instantiate a JSF <code>ValueChangeListener</code>.
176 * </p>
177 */
178 private TreeSet valueChangeListeners = new TreeSet();
179
180 /***
181 * <p>Reference to a set of associated {@link ComponentBean} that is an instance of
182 * {@link ActionListenerBean} and is used to instantiate a JSF <code>ActionListener</code>.
183 * </p>
184 */
185 private TreeSet actionListeners = new TreeSet();
186
187 /***
188 * <p>This attribute used when defining the template style of page composition where the
189 * body of the HTML element is rendered by the component ignoring the HTML.
190 */
191 private String allowBody = null;
192
193 /***
194 * <p>Use this property to add the component to the parent's facet collection rather than
195 * the default children collection.</p>
196 */
197 private String facetName = null;
198
199 /***
200 * <p>The replacement symbol table for the component meta-data.</p>
201 */
202 private Map symbols = new Attributes();
203
204 /***
205 * <p>This property only applies when using the {@link org.apache.shale.clay.component.Clay}
206 * template features. A <code>true</code> value is returned if the HTML child nodes under
207 * the node that this meta component is bound to should be rendered; otherwise, a <code>"false"</code>
208 * value is returned indicating the child nodes should be ignored.
209 * </p>
210 *
211 * @return literal string "true" if allow body is on
212 */
213 public String getAllowBody() {
214 return allowBody;
215 }
216
217 /***
218 * <p>This property only applies when using the {@link org.apache.shale.clay.component.Clay}
219 * template features. Sets a Boolean string value that indicating if the child HTML nodes
220 * under the node that this component is bound to should render or ignore its child nodes.
221 * </p>
222 *
223 * @param allowBody indicates how the child markup nodes are processed
224 *
225 */
226 public void setAllowBody(String allowBody) {
227 this.allowBody = allowBody;
228 }
229
230 /***
231 * <p>Returns a boolean representation of the <code>allowBody</code> property.
232 * The default is <code>true</code></p>
233 *
234 * @return <code>true</code> if allowBody has a literal string value of "true"
235 */
236 public boolean getIsBodyAllowed() {
237 boolean f = true;
238 try {
239 if (allowBody != null) {
240 f = Boolean.valueOf(allowBody).booleanValue();
241 }
242 } catch (Exception e) {
243 f = true;
244 }
245
246 return f;
247 }
248
249 /***
250 * <p>Returns the facet name that will be used as the identifier when adding the
251 * component to the parent facets collection.</p>
252 *
253 * @return facetName
254 */
255 public String getFacetName() {
256 return facetName;
257 }
258
259 /***
260 * <p>Sets the facet name that will be used as the identifier when adding the
261 * component to the parent facets collection.</p>
262 *
263 * @param facetName component grouping
264 */
265 public void setFacetName(String facetName) {
266 this.facetName = facetName;
267 }
268
269 /***
270 * @return a value list of the object's state
271 */
272 public String toString() {
273 StringBuffer buff = new StringBuffer();
274 buff.append("jsfid=\"").append(jsfid).append(
275 "\" componentType=\"").append(componentType).append(
276 "\" extends=\"").append(extendsElementId).append("\"")
277 .append(" allowBody=\"").append(allowBody)
278 .append("\" ").append("facetName=\"").append(facetName)
279 .append("\"");
280
281 return buff.toString();
282 }
283
284 /***
285 * <p>Returns the component type that is used to instantiate the associated
286 * JSF component.<p>
287 *
288 * @return component type
289 */
290 public String getComponentType() {
291 return componentType;
292 }
293
294 /***
295 * <p>Returns the <code>jsfid</code> of the meta component that this
296 * instance inherits from.<p>
297 *
298 * @return extended jsfid
299 */
300 public String getExtends() {
301 return extendsElementId;
302 }
303
304 /***
305 * <p>Sets the component type uses by abstract factories to instantiate
306 * associated JSF resources.</p>
307 *
308 * @param componentType used to create a JSF resource
309 */
310 public void setComponentType(String componentType) {
311 this.componentType = componentType;
312 }
313
314 /***
315 * <p>Sets the <code>jsfid</code> of the meta component that this meta
316 * component inherits from.
317 * </p>
318 *
319 * @param extendsElementId extending jsfid
320 */
321 public void setExtends(String extendsElementId) {
322 this.extendsElementId = extendsElementId;
323 }
324
325 /***
326 * <p>Returns a <code>Iterator</code> to the <code>children</code> set.
327 * Each item in the set is uniquely identified by its
328 * <code>renderId</code> property and an instance of {@link ElementBean}.
329 * </p>
330 *
331 * @return iterator for the children collection
332 */
333 public Iterator getChildrenIterator() {
334
335 return children.iterator();
336 }
337
338 /***
339 *<p>Returns a set of children that are instances of {@link ElementBean}.
340 *</p>
341 *
342 * @return children collection
343 */
344 public Collection getChildren() {
345 return children;
346 }
347
348 /***
349 * <p>Merges two sets of children {@link org.apache.shale.clay.config.beans.ElementBean}. Items in the source
350 * collection will replace those in the target with the same <code>renderId</code>
351 * </p>
352 *
353 * @param collection of child components
354 */
355 public void setChildren(Collection collection) {
356 children.addAll(collection);
357 }
358
359 /***
360 * <p>Adds a child {@link org.apache.shale.clay.config.beans.ElementBean} to the <code>children</code> set and
361 * fixes up the composition parent relationship.</p>
362 *
363 * @param obj element bean added as a child
364 */
365 public void addChild(ElementBean obj) {
366 if (obj.getJsfid() != null) {
367 obj.setHasAParent(this);
368 children.add(obj);
369 } else {
370 log.error(messages.getMessage("missing.jsfid.error", new Object[] {"ElementBean.jsfid", getJsfid()}));
371 }
372 }
373
374 /***
375 * <p>This <code>Comparable</code> implementation makes the
376 * <code>jsfid</code> attribute the unique identifier for the object in a
377 * set.</p>
378 *
379 * @param obj target object to compare to
380 * @return weighted value based on the jsfid property
381 */
382 public int compareTo(Object obj) {
383 return ((ComponentBean) obj).getJsfid().compareTo(
384 getJsfid());
385 }
386
387 /***
388 * <p>Gets a meta converter bean used to instantiate a jsf <code>Converter</code>.</p>
389 *
390 * @return converter assigned to the component
391 */
392 public ComponentBean getConverter() {
393 return converter;
394 }
395
396 /***
397 * <p>Adds a {@link ConverterBean} and assigns the composition parent.</p>
398 *
399 * @param bean converter assigned to this component
400 */
401 public void addConverter(ConverterBean bean) {
402 if (bean.getJsfid() != null) {
403 bean.setHasAParent(this);
404 converter = bean;
405 } else {
406 log.error(messages.getMessage("missing.jsfid.error", new Object[] {"ConverterBean.jsfid", getJsfid()}));
407 }
408 }
409
410 /***
411 * <p>Returns a <code>Collection</code> of meta validators used to create jsf <code>Validator</code>
412 * object instances.
413 *
414 * @return collection of validators
415 */
416 public Collection getValidators() {
417 return validators;
418 }
419
420 /***
421 * <p>Adds a collection of {@link ValidatorBean} to the <code>validator</code> set. Each
422 * instance is uniquely identified in the collection by the <code>jsfid</code>.
423 * </p>
424 *
425 * @param collection of validators added to the component
426 */
427 public void setValidators(Collection collection) {
428 validators.addAll(collection);
429 }
430
431 /***
432 * <p>Returns a <code>Iterator</code> to the <code>validator</code> set. Each
433 * object will be an instance of {@link ValidatorBean}.
434 * </p>
435 *
436 * @return Iterator of the components validators collection
437 */
438 public Iterator getValidatorIterator() {
439
440 return validators.iterator();
441 }
442
443 /***
444 * <p>Adds a {@link ValidatorBean} and assigns the composition parent.</p>
445 *
446 * @param bean validator to add to the component
447 */
448 public void addValidator(ValidatorBean bean) {
449 if (bean.getJsfid() != null) {
450 validators.add(bean);
451 } else {
452 log.error(messages.getMessage("missing.jsfid.error",
453 new Object[] {"ValidatorBean.jsfid", getJsfid()}));
454 }
455 }
456
457 /***
458 * <p>Returns a <code>Iterator</code> to the <code>valueChangeListeners</code> set.
459 * Each {@link ValueChangeListenerBean} in the collection is uniquely identified
460 * by <code>jsfid</code>.
461 *</p>
462 *
463 * @return collection of value change listeners
464 */
465 public Collection getValueChangeListeners() {
466 return valueChangeListeners;
467 }
468
469 /***
470 * <p>Merges a collection of {@link org.apache.shale.clay.config.beans.ValueChangeListenerBean} where items in the
471 * source collection with the same <code>jsfid</code> will override items
472 * in the target set with the same identifier.
473 * </p>
474 *
475 * @param collection of value change listeners added to the component
476 */
477 public void setValueChangeListeners(Collection collection) {
478 valueChangeListeners.addAll(collection);
479 }
480
481 /***
482 * <p>Returns a <code>Iterator</code> for the <code>valueChangeListener</code> set
483 * of {@link ValueChangeListenerBean}.<p>
484 *
485 * @return iterator for the collection of value change listeners
486 */
487 public Iterator getValueChangeListenerIterator() {
488 return valueChangeListeners.iterator();
489 }
490
491 /***
492 * <p>Adds a {@link ValueChangeListenerBean} to the set where each instance is
493 * uniquely identified by <code>jsfid</code>.</p>
494 *
495 * @param bean value change listener added to the components collection of listeners
496 */
497 public void addValueChangeListener(ValueChangeListenerBean bean) {
498 if (bean.getJsfid() != null) {
499 valueChangeListeners.add(bean);
500 } else {
501 log.error(messages.getMessage("missing.jsfid.error",
502 new Object[] {"ValueChangeListenerBean.jsfid", getJsfid()}));
503 }
504 }
505
506 /***
507 * <p>Returns a <code>Collection</code> of {@link ActionListenerBean}. </p>
508 *
509 * @return collection of the component's action listeners
510 */
511 public Collection getActionListeners() {
512 return actionListeners;
513 }
514
515 /***
516 * <p>Merges two collections where items in the source collection will override
517 * those in the target collection of {@link ActionListenerBean} by the <code>jsfid</code>
518 * property.</p>
519 *
520 * @param collection of action listeners added to the components set
521 */
522 public void setActionListeners(Collection collection) {
523 actionListeners.addAll(collection);
524 }
525
526 /***
527 * <p>Returns an <code>Iterator</code> for the <code>actionListeners</code> set of
528 * {@link ActionListenerBean}.
529 * </p>
530 *
531 * @return iterator of the component's action listeners set
532 */
533 public Iterator getActionListenerIterator() {
534
535 return actionListeners.iterator();
536 }
537
538 /***
539 * <p>Adds an {@link ActionListenerBean} to the <code>actionListeners</code> set. Each
540 * instance is uniquely identified by the <code>jsfid</code> property.
541 * </p>
542 *
543 * @param bean action listener added to the component
544 */
545 public void addActionListener(ActionListenerBean bean) {
546 if (bean.getJsfid() != null) {
547 actionListeners.add(bean);
548 } else {
549 log.error(messages.getMessage("missing.jsfid.error",
550 new Object[] {"ActionListenerBean.jsfid", getJsfid()}));
551 }
552 }
553
554 /***
555 * <p>This inner class provides implementation for an <code>Iterator</code> handeling
556 * {@link AttributeBean} objects in the <code>attributes</code> collection.
557 * </p>
558 *
559 * @return iterator for the components attributes Map
560 */
561 public Iterator getAttributeIterator() {
562
563 return new Iterator() {
564 /***
565 * <p>Graps a <code>Iterator</code> instance of the attributes entry set</p>
566 */
567 private Iterator entrySet = getAttributes().entrySet().iterator();
568 /***
569 * <p>Decorates the <code>Iterator</code> of the private <code>entrySet</code></p>
570 */
571 public boolean hasNext() {
572 return entrySet.hasNext();
573 }
574 /***
575 * <p>Returns the next {@link AttributeBean} in the Map collection</p>
576 */
577 public Object next() {
578 Map.Entry e = (Map.Entry) entrySet.next();
579 return e.getValue();
580 }
581
582 /***
583 * <p>This method is not applicable for this class and has an empty
584 * method body but has to be implemented to realize the <code>Iterator</code>
585 * interface.
586 * </p>
587 */
588 public void remove() {
589 }
590 };
591
592 }
593
594 /***
595 * <p>Returns a {@link AttributeBean} by the classes <code>name</code> property.</p>
596 *
597 * @param key attribute name
598 * @return attribute bean for the key
599 */
600 public AttributeBean getAttribute(String key) {
601 return (AttributeBean) getAttributes().get(key);
602 }
603
604 /***
605 * <p>Adds a {@link AttributeBean} to the <code>attributes</code> Map collection where
606 * the <code>name</code> property is the key identifier in the value pair relationship.
607 * </p>
608 *
609 * @param obj attribute bean added to the attributes Map
610 */
611 public void addAttribute(AttributeBean obj) {
612 if (obj.getName() != null) {
613 obj.setHasAParent(this);
614 attributes.put(obj.getName(), obj);
615 } else {
616 log.error(messages.getMessage("missing.jsfid.error", new Object[] {"AttributeBean.jsfid", getJsfid()}));
617 }
618
619 }
620
621 /***
622 * <p>Returns the a <code>Map</code> collection of {@link AttributeBean} objects.</p>
623 *
624 * @return attributes map
625 */
626 public Map getAttributes() {
627
628 return attributes;
629 }
630
631 /***
632 * <p>Returns the unique meta component identifier.</p>
633 *
634 * @return jsfid
635 */
636 public String getJsfid() {
637 return jsfid;
638 }
639
640 /***
641 * <p>Merges a set of {@link AttributeBean} where items in the source
642 * collection override items in the target collection by the object's
643 * <code>jsfid</code> property.
644 * </p>
645 *
646 * @param map of attributes to be merged
647 */
648 public void setAttributes(Map map) {
649 attributes.putAll(map);
650 }
651
652 /***
653 * <p>Sets the unique meta component identifier.</p>
654 *
655 * @param jsfid identifier
656 */
657 public void setJsfid(String jsfid) {
658 this.jsfid = jsfid;
659 }
660
661 /***
662 * <p>Returns the parent component that aggregates this object.</p>
663 *
664 * @return composition parent
665 */
666 public ComponentBean getHasAParent() {
667 return hasAParent;
668 }
669
670 /***
671 * <p>Returns the parent component that generalizes this object.</p>
672 *
673 * @return inheritance parent
674 */
675 public ComponentBean getIsAParent() {
676 return isAParent;
677 }
678
679 /***
680 * <p>Sets the parent that owns this component.</p>
681 *
682 * @param bean composition parent
683 */
684 public void setHasAParent(ComponentBean bean) {
685 hasAParent = bean;
686 }
687
688 /***
689 * <p>Sets the component that this instance extends.</p>
690 *
691 * @param bean inheritance parent
692 */
693 public void setIsAParent(ComponentBean bean) {
694 isAParent = bean;
695 }
696
697 /***
698 * <p>Returns a xpath like string describing how this component
699 * fits into the overall composition.</p>
700 *
701 * @return composition client id
702 */
703 public StringBuffer getHasAClientId() {
704 StringBuffer id = null;
705
706 if (getHasAParent() != null) {
707 id = getHasAParent().getHasAClientId();
708 } else {
709 id = new StringBuffer();
710 }
711
712 id.append("/").append(getJsfid());
713
714 return id;
715 }
716
717 /***
718 * <p>Returns an xpath like string that describes the heritage
719 * of this component.</p>
720 *
721 * @return inheritance client id
722 */
723 public StringBuffer getIsAClientId() {
724 StringBuffer id = new StringBuffer();
725
726 ComponentBean parent = getIsAParent();
727 while (parent != null) {
728 id.insert(0, parent.getJsfid() + (id.length() > 0 ? ":" : ""));
729 parent = parent.getIsAParent();
730 }
731 parent = null;
732
733 return id;
734 }
735
736 /***
737 * <p>Returns a boolean flag indicating that the meta inheritances
738 * has been resolved.</p>
739 *
740 * @return <code>true</code> if inheritance has been resolved
741 */
742 public boolean isInheritanceFinal() {
743 return isInheritanceFinal;
744 }
745
746 /***
747 * <p>Sets a boolean flag indicating that the meta inheritances
748 * have been resolved.</p>
749 *
750 * @param b <code>true</code> if inheritance has been resolved
751 */
752 public void setInheritanceFinal(boolean b) {
753 isInheritanceFinal = b;
754 }
755
756 /***
757 * <p>Returns the identifier that will populate the JSF <code>UIComponent.id</code>
758 * property and is used to name the component within the tree.
759 * </p>
760 *
761 * @return component's id
762 */
763 public String getId() {
764 AttributeBean attr = (AttributeBean) attributes.get("id");
765 if (attr != null) {
766 return attr.getValue();
767 }
768
769 return null;
770 }
771
772 /***
773 * <p>Sets the identifier that is used to populate the JSF <code>UIComponent.id</code>
774 * property. </p>
775 *
776 * @param id component's identifier
777 */
778 public void setId(String id) {
779 AttributeBean attr = new AttributeBean();
780 attr.setBindingType(AttributeBean.BINDING_TYPE_NONE);
781 attr.setValue(id);
782 attr.setName("id");
783 addAttribute(attr);
784 }
785
786 /***
787 * <p>Adds a symbol identified by the
788 * {@link SymbolBean} to the symbols collection.</p>
789 *
790 * @param symbol added to the symbols Map
791 */
792 public void addSymbol(SymbolBean symbol) {
793 if (symbol.getName() != null && symbol.getName().length() > 0) {
794 StringBuffer buff = new StringBuffer(symbol.getName());
795 if (buff.charAt(0) != '@') {
796 buff.insert(0, '@');
797 symbol.setName(buff.toString());
798 }
799
800 symbols.put(symbol.getName(), symbol);
801 }
802 }
803
804 /***
805 * <p>Returns the replacement symbols assigned to the component.
806 * The key value represents the literal replacement string.
807 * The value Map property represents target {@link SymbolBean}.</p>
808 *
809 * @return map of symbols
810 */
811 public Map getSymbols() {
812 return symbols;
813 }
814
815 /***
816 * <p>Returns a {@link SymbolBean} from the <code>symbols</code>
817 * Map by <code>name</code>. Prepends a '@' character to the
818 * <code>name</code> if it doesn't exist.</p>
819 *
820 * @param name of the symbol
821 * @return symbol bean identified by name
822 */
823 public SymbolBean getSymbol(String name) {
824 StringBuffer tmp = new StringBuffer(name);
825 if (tmp.charAt(0) != '@') {
826 tmp.insert(0, '@');
827 }
828
829 SymbolBean symbol = (SymbolBean) symbols.get(tmp.toString());
830
831 return symbol;
832 }
833
834 }
835