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