0001: /*
0002: * Copyright 2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package javax.faces.component;
0017:
0018: import org.apache.commons.logging.Log;
0019: import org.apache.commons.logging.LogFactory;
0020:
0021: import javax.el.ValueExpression;
0022: import javax.faces.FacesException;
0023: import javax.faces.FactoryFinder;
0024: import javax.faces.context.FacesContext;
0025: import javax.faces.el.ValueBinding;
0026: import javax.faces.event.AbortProcessingException;
0027: import javax.faces.event.FacesEvent;
0028: import javax.faces.event.FacesListener;
0029: import javax.faces.render.RenderKit;
0030: import javax.faces.render.RenderKitFactory;
0031: import javax.faces.render.Renderer;
0032: import java.io.IOException;
0033: import java.io.Serializable;
0034: import java.lang.reflect.Array;
0035: import java.util.ArrayList;
0036: import java.util.HashMap;
0037: import java.util.Iterator;
0038: import java.util.List;
0039: import java.util.Map;
0040: import java.util.Map.Entry;
0041:
0042: /**
0043: * Standard implementation of the UIComponent base class; all standard JSF
0044: * components extend this class.
0045: * <p>
0046: * <i>Disclaimer</i>: The official definition for the behaviour of
0047: * this class is the JSF 1.1 specification but for legal reasons the
0048: * specification cannot be replicated here. Any javadoc here therefore
0049: * describes the current implementation rather than the spec, though
0050: * this class has been verified as correctly implementing the spec.
0051: *
0052: * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a> for more.
0053: *
0054: * @author Manfred Geiler (latest modification by $Author: mbr $)
0055: * @version $Revision: 519178 $ $Date: 2007-03-17 00:19:41 +0100 (Sa, 17 Mrz 2007) $
0056: */
0057: public abstract class UIComponentBase extends UIComponent {
0058: private static Log log = LogFactory.getLog(UIComponentBase.class);
0059:
0060: private _ComponentAttributesMap _attributesMap = null;
0061: private List<UIComponent> _childrenList = null;
0062: private Map<String, UIComponent> _facetMap = null;
0063: private List<FacesListener> _facesListeners = null;
0064: private String _clientId = null;
0065: private String _id = null;
0066: private UIComponent _parent = null;
0067: private boolean _transient = false;
0068:
0069: public UIComponentBase() {
0070: }
0071:
0072: /**
0073: * Get a map through which all the UIComponent's properties, value-bindings
0074: * and non-property attributes can be read and written.
0075: * <p>
0076: * When writing to the returned map:
0077: * <ul>
0078: * <li>If this component has an explicit property for the specified key
0079: * then the setter method is called. An IllegalArgumentException is
0080: * thrown if the property is read-only. If the property is readable
0081: * then the old value is returned, otherwise null is returned.
0082: * <li>Otherwise the key/value pair is stored in a map associated with
0083: * the component.
0084: * </ul>
0085: * Note that value-bindings are <i>not</i> written by put calls to this map.
0086: * Writing to the attributes map using a key for which a value-binding
0087: * exists will just store the value in the attributes map rather than
0088: * evaluating the binding, effectively "hiding" the value-binding from
0089: * later attributes.get calls. Setter methods on components commonly do
0090: * <i>not</i> evaluate a binding of the same name; they just store the
0091: * provided value directly on the component.
0092: * <p>
0093: * When reading from the returned map:
0094: * <ul>
0095: * <li>If this component has an explicit property for the specified key
0096: * then the getter method is called. If the property exists, but is
0097: * read-only (ie only a setter method is defined) then an
0098: * IllegalArgumentException is thrown.
0099: * <li>If the attribute map associated with the component has an entry
0100: * with the specified key, then that is returned.
0101: * <li>If this component has a value-binding for the specified key, then
0102: * the value-binding is evaluated to fetch the value.
0103: * <li>Otherwise, null is returned.
0104: * </ul>
0105: * Note that components commonly define getter methods such that they
0106: * evaluate a value-binding of the same name if there isn't yet a
0107: * local property.
0108: * <p>
0109: * Assigning values to the map which are not explicit properties on
0110: * the underlying component can be used to "tunnel" attributes from
0111: * the JSP tag (or view-specific equivalent) to the associated renderer
0112: * without modifying the component itself.
0113: * <p>
0114: * Any value-bindings and non-property attributes stored in this map
0115: * are automatically serialized along with the component when the view
0116: * is serialized.
0117: */
0118: public Map<String, Object> getAttributes() {
0119: if (_attributesMap == null) {
0120: _attributesMap = new _ComponentAttributesMap(this );
0121: }
0122: return _attributesMap;
0123: }
0124:
0125: /**
0126: * Get the named value-binding associated with this component.
0127: * <p>
0128: * Value-bindings are stored in a map associated with the component,
0129: * though there is commonly a property (setter/getter methods)
0130: * of the same name defined on the component itself which
0131: * evaluates the value-binding when called.
0132: *
0133: * @deprecated Replaced by getValueExpression
0134: */
0135: public ValueBinding getValueBinding(String name) {
0136: ValueExpression expression = getValueExpression(name);
0137: if (expression != null) {
0138: if (expression instanceof _ValueBindingToValueExpression) {
0139: return ((_ValueBindingToValueExpression) expression)
0140: .getValueBinding();
0141: }
0142: return new _ValueExpressionToValueBinding(expression);
0143: }
0144: return null;
0145: }
0146:
0147: /**
0148: * Put the provided value-binding into a map of value-bindings
0149: * associated with this component.
0150: *
0151: * @deprecated Replaced by setValueExpression
0152: */
0153: public void setValueBinding(String name, ValueBinding binding) {
0154: setValueExpression(name, binding == null ? null
0155: : new _ValueBindingToValueExpression(binding));
0156: }
0157:
0158: /**
0159: * Get a string which can be output to the response which uniquely
0160: * identifies this UIComponent within the current view.
0161: * <p>
0162: * The component should have an id attribute already assigned to it;
0163: * however if the id property is currently null then a unique id
0164: * is generated and set for this component. This only happens when
0165: * components are programmatically created without ids, as components
0166: * created by a ViewHandler should be assigned ids when they are created.
0167: * <p>
0168: * If this component is a descendant of a NamingContainer then the
0169: * client id is of form "{namingContainerId}:{componentId}". Note that
0170: * the naming container's id may itself be of compound form if it has
0171: * an ancestor naming container. Note also that this only applies to
0172: * naming containers; other UIComponent types in the component's
0173: * ancestry do not affect the clientId.
0174: * <p>
0175: * Finally the renderer associated with this component is asked to
0176: * convert the id into a suitable form. This allows escaping of any
0177: * characters in the clientId which are significant for the markup
0178: * language generated by that renderer.
0179: */
0180: public String getClientId(FacesContext context) {
0181: if (context == null)
0182: throw new NullPointerException("context");
0183:
0184: if (_clientId != null)
0185: return _clientId;
0186:
0187: boolean idWasNull = false;
0188: String id = getId();
0189: if (id == null) {
0190: //Although this is an error prone side effect, we automatically create a new id
0191: //just to be compatible to the RI
0192: UIViewRoot viewRoot = context.getViewRoot();
0193: if (viewRoot != null) {
0194: id = viewRoot.createUniqueId();
0195: } else {
0196: // The RI throws a NPE
0197: throw new FacesException(
0198: "Cannot create clientId. No id is assigned for component to create an id and UIViewRoot is not defined: "
0199: + getPathToComponent(this ));
0200: }
0201: setId(id);
0202: //We remember that the id was null and log a warning down below
0203: idWasNull = true;
0204: }
0205:
0206: UIComponent namingContainer = _ComponentUtils
0207: .findParentNamingContainer(this , false);
0208: if (namingContainer != null) {
0209: _clientId = namingContainer.getContainerClientId(context)
0210: + NamingContainer.SEPARATOR_CHAR + id;
0211: } else {
0212: _clientId = id;
0213: }
0214:
0215: Renderer renderer = getRenderer(context);
0216: if (renderer != null) {
0217: _clientId = renderer.convertClientId(context, _clientId);
0218: }
0219:
0220: if (idWasNull && log.isWarnEnabled()) {
0221: log
0222: .warn("WARNING: Component "
0223: + _clientId
0224: + " just got an automatic id, because there was no id assigned yet. "
0225: + "If this component was created dynamically (i.e. not by a JSP tag) you should assign it an "
0226: + "explicit static id or assign it the id you get from "
0227: + "the createUniqueId from the current UIViewRoot "
0228: + "component right after creation! Path to Component: "
0229: + getPathToComponent(this ));
0230: }
0231:
0232: return _clientId;
0233: }
0234:
0235: /**
0236: * Get a string which uniquely identifies this UIComponent within the scope of the nearest ancestor NamingContainer
0237: * component. The id is not necessarily unique across all components in the current view.
0238: */
0239: public String getId() {
0240: return _id;
0241: }
0242:
0243: /**
0244: * <code>invokeOnComponent</code> must be implemented in <code>UIComponentBase</code> too...
0245: */
0246: public boolean invokeOnComponent(FacesContext context,
0247: String clientId, ContextCallback callback)
0248: throws FacesException {
0249: return super .invokeOnComponent(context, clientId, callback);
0250: }
0251:
0252: /**
0253: * Set an identifier for this component which is unique within the
0254: * scope of the nearest ancestor NamingContainer component. The id is
0255: * not necessarily unique across all components in the current view.
0256: * <p>
0257: * The id must start with an underscore if it is generated by the JSF
0258: * framework, and must <i>not</i> start with an underscore if it has
0259: * been specified by the user (eg in a JSP tag).
0260: * <p>
0261: * The first character of the id must be an underscore or letter.
0262: * Following characters may be letters, digits, underscores or dashes.
0263: * <p>
0264: * Null is allowed as a parameter, and will reset the id to null.
0265: * <p>
0266: * The clientId of this component is reset by this method; see
0267: * getClientId for more info.
0268: *
0269: * @throws IllegalArgumentException if the id is not valid.
0270: */
0271: public void setId(String id) {
0272: isIdValid(id);
0273: _id = id;
0274: _clientId = null;
0275: }
0276:
0277: public UIComponent getParent() {
0278: return _parent;
0279: }
0280:
0281: public void setParent(UIComponent parent) {
0282: _parent = parent;
0283: }
0284:
0285: /**
0286: * Indicates whether this component or its renderer manages the
0287: * invocation of the rendering methods of its child components.
0288: * When this is true:
0289: * <ul>
0290: * <li>This component's encodeBegin method will only be called
0291: * after all the child components have been created and added
0292: * to this component.
0293: * <li>This component's encodeChildren method will be called
0294: * after its encodeBegin method. Components for which this
0295: * method returns false do not get this method invoked at all.
0296: * <li>No rendering methods will be called automatically on
0297: * child components; this component is required to invoke the
0298: * encodeBegin/encodeEnd/etc on them itself.
0299: * </ul>
0300: */
0301: public boolean getRendersChildren() {
0302: Renderer renderer = getRenderer(getFacesContext());
0303: return renderer != null ? renderer.getRendersChildren() : false;
0304: }
0305:
0306: /**
0307: * Return a list of the UIComponent objects which are direct children
0308: * of this component.
0309: * <p>
0310: * The list object returned has some non-standard behaviour:
0311: * <ul>
0312: * <li>The list is type-checked; only UIComponent objects can be added.
0313: * <li>If a component is added to the list with an id which is the same
0314: * as some other component in the list then an exception is thrown. However
0315: * multiple components with a null id may be added.
0316: * <li>The component's parent property is set to this component. If the
0317: * component already had a parent, then the component is first removed
0318: * from its original parent's child list.
0319: * </ul>
0320: */
0321: public List<UIComponent> getChildren() {
0322: if (_childrenList == null) {
0323: _childrenList = new _ComponentChildrenList(this );
0324: }
0325: return _childrenList;
0326: }
0327:
0328: /**
0329: * Return the number of direct child components this component has.
0330: * <p>
0331: * Identical to getChildren().size() except that when this component
0332: * has no children this method will not force an empty list to be
0333: * created.
0334: */
0335: public int getChildCount() {
0336: return _childrenList == null ? 0 : _childrenList.size();
0337: }
0338:
0339: /**
0340: * Standard method for finding other components by id, inherited by
0341: * most UIComponent objects.
0342: * <p>
0343: * The lookup is performed in a manner similar to finding a file
0344: * in a filesystem; there is a "base" at which to start, and the
0345: * id can be for something in the "local directory", or can include
0346: * a relative path. Here, NamingContainer components fill the role
0347: * of directories, and ":" is the "path separator". Note, however,
0348: * that although components have a strict parent/child hierarchy,
0349: * component ids are only prefixed ("namespaced") with the id of
0350: * their parent when the parent is a NamingContainer.
0351: * <p>
0352: * The base node at which the search starts is determined as
0353: * follows:
0354: * <ul>
0355: * <li>When expr starts with ':', the search starts with the root
0356: * component of the tree that this component is in (ie the ancestor
0357: * whose parent is null).
0358: * <li>Otherwise, if this component is a NamingContainer then the search
0359: * starts with this component.
0360: * <li>Otherwise, the search starts from the nearest ancestor
0361: * NamingContainer (or the root component if there is no NamingContainer
0362: * ancestor).
0363: * </ul>
0364: *
0365: * @param expr is of form "id1:id2:id3".
0366: * @return UIComponent or null if no component with the specified id is
0367: * found.
0368: */
0369:
0370: public UIComponent findComponent(String expr) {
0371: if (expr == null)
0372: throw new NullPointerException("expr");
0373: if (expr.length() == 0)
0374: return null;
0375:
0376: UIComponent findBase;
0377: if (expr.charAt(0) == NamingContainer.SEPARATOR_CHAR) {
0378: findBase = _ComponentUtils.getRootComponent(this );
0379: expr = expr.substring(1);
0380: } else {
0381: if (this instanceof NamingContainer) {
0382: findBase = this ;
0383: } else {
0384: findBase = _ComponentUtils.findParentNamingContainer(
0385: this , true /* root if not found */);
0386: }
0387: }
0388:
0389: int separator = expr.indexOf(NamingContainer.SEPARATOR_CHAR);
0390: if (separator == -1) {
0391: return _ComponentUtils.findComponent(findBase, expr);
0392: }
0393:
0394: String id = expr.substring(0, separator);
0395: findBase = _ComponentUtils.findComponent(findBase, id);
0396: if (findBase == null) {
0397: return null;
0398: }
0399:
0400: if (!(findBase instanceof NamingContainer))
0401: throw new IllegalArgumentException(
0402: "Intermediate identifier "
0403: + id
0404: + " in search expression "
0405: + expr
0406: + " identifies a UIComponent that is not a NamingContainer");
0407:
0408: return findBase.findComponent(expr.substring(separator + 1));
0409:
0410: }
0411:
0412: public Map<String, UIComponent> getFacets() {
0413: if (_facetMap == null) {
0414: _facetMap = new _ComponentFacetMap<UIComponent>(this );
0415: }
0416: return _facetMap;
0417: }
0418:
0419: public UIComponent getFacet(String name) {
0420: return _facetMap == null ? null : _facetMap.get(name);
0421: }
0422:
0423: public Iterator<UIComponent> getFacetsAndChildren() {
0424: return new _FacetsAndChildrenIterator<UIComponent>(_facetMap,
0425: _childrenList);
0426: }
0427:
0428: /**
0429: * Invoke any listeners attached to this object which are listening
0430: * for an event whose type matches the specified event's runtime
0431: * type.
0432: * <p>
0433: * This method does not propagate the event up to parent components,
0434: * ie listeners attached to parent components don't automatically
0435: * get called.
0436: * <p>
0437: * If any of the listeners throws AbortProcessingException then
0438: * that exception will prevent any further listener callbacks
0439: * from occurring, and the exception propagates out of this
0440: * method without alteration.
0441: * <p>
0442: * ActionEvent events are typically queued by the renderer associated
0443: * with this component in its decode method; ValueChangeEvent events by
0444: * the component's validate method. In either case the event's source
0445: * property references a component. At some later time the UIViewRoot
0446: * component iterates over its queued events and invokes the broadcast
0447: * method on each event's source object.
0448: *
0449: * @param event must not be null.
0450: */
0451: public void broadcast(FacesEvent event)
0452: throws AbortProcessingException {
0453: if (event == null)
0454: throw new NullPointerException("event");
0455: if (_facesListeners == null)
0456: return;
0457: for (Iterator<FacesListener> it = _facesListeners.iterator(); it
0458: .hasNext();) {
0459: FacesListener facesListener = it.next();
0460: if (event.isAppropriateListener(facesListener)) {
0461: event.processListener(facesListener);
0462: }
0463: }
0464: }
0465:
0466: /**
0467: * Check the submitted form parameters for data associated with this
0468: * component. This default implementation delegates to this component's
0469: * renderer if there is one, and otherwise ignores the call.
0470: */
0471: public void decode(FacesContext context) {
0472: if (context == null)
0473: throw new NullPointerException("context");
0474: Renderer renderer = getRenderer(context);
0475: if (renderer != null) {
0476: renderer.decode(context, this );
0477: }
0478: }
0479:
0480: public void encodeBegin(FacesContext context) throws IOException {
0481: if (context == null)
0482: throw new NullPointerException("context");
0483: if (!isRendered())
0484: return;
0485: Renderer renderer = getRenderer(context);
0486: if (renderer != null) {
0487: renderer.encodeBegin(context, this );
0488: }
0489: }
0490:
0491: public void encodeChildren(FacesContext context) throws IOException {
0492: if (context == null)
0493: throw new NullPointerException("context");
0494: if (!isRendered())
0495: return;
0496: Renderer renderer = getRenderer(context);
0497: if (renderer != null) {
0498: renderer.encodeChildren(context, this );
0499: }
0500: }
0501:
0502: public void encodeEnd(FacesContext context) throws IOException {
0503: if (context == null)
0504: throw new NullPointerException("context");
0505: if (!isRendered())
0506: return;
0507: Renderer renderer = getRenderer(context);
0508: if (renderer != null) {
0509: renderer.encodeEnd(context, this );
0510: }
0511: }
0512:
0513: protected void addFacesListener(FacesListener listener) {
0514: if (listener == null)
0515: throw new NullPointerException("listener");
0516: if (_facesListeners == null) {
0517: _facesListeners = new ArrayList<FacesListener>();
0518: }
0519: _facesListeners.add(listener);
0520: }
0521:
0522: protected FacesListener[] getFacesListeners(Class clazz) {
0523: if (clazz == null) {
0524: throw new NullPointerException("Class is null");
0525: }
0526: if (!FacesListener.class.isAssignableFrom(clazz)) {
0527: throw new IllegalArgumentException("Class "
0528: + clazz.getName() + " must implement "
0529: + FacesListener.class);
0530: }
0531:
0532: if (_facesListeners == null) {
0533: return (FacesListener[]) Array.newInstance(clazz, 0);
0534: }
0535: List<FacesListener> lst = null;
0536: for (Iterator<FacesListener> it = _facesListeners.iterator(); it
0537: .hasNext();) {
0538: FacesListener facesListener = it.next();
0539: if (clazz.isAssignableFrom(facesListener.getClass())) {
0540: if (lst == null)
0541: lst = new ArrayList<FacesListener>();
0542: lst.add(facesListener);
0543: }
0544: }
0545: if (lst == null) {
0546: return (FacesListener[]) Array.newInstance(clazz, 0);
0547: }
0548:
0549: return lst.toArray((FacesListener[]) Array.newInstance(clazz,
0550: lst.size()));
0551: }
0552:
0553: protected void removeFacesListener(FacesListener listener) {
0554: if (listener == null) {
0555: throw new NullPointerException("listener is null");
0556: }
0557:
0558: if (_facesListeners != null) {
0559: _facesListeners.remove(listener);
0560: }
0561: }
0562:
0563: public void queueEvent(FacesEvent event) {
0564: if (event == null)
0565: throw new NullPointerException("event");
0566: UIComponent parent = getParent();
0567: if (parent == null) {
0568: throw new IllegalStateException(
0569: "component is not a descendant of a UIViewRoot");
0570: }
0571: parent.queueEvent(event);
0572: }
0573:
0574: public void processDecodes(FacesContext context) {
0575: if (context == null)
0576: throw new NullPointerException("context");
0577: if (!isRendered())
0578: return;
0579: for (Iterator<UIComponent> it = getFacetsAndChildren(); it
0580: .hasNext();) {
0581: it.next().processDecodes(context);
0582: }
0583: try {
0584: decode(context);
0585: } catch (RuntimeException e) {
0586: context.renderResponse();
0587: throw e;
0588: }
0589: }
0590:
0591: public void processValidators(FacesContext context) {
0592: if (context == null)
0593: throw new NullPointerException("context");
0594: if (!isRendered())
0595: return;
0596:
0597: for (Iterator<UIComponent> it = getFacetsAndChildren(); it
0598: .hasNext();) {
0599: it.next().processValidators(context);
0600: }
0601: }
0602:
0603: /**
0604: * This isn't an input component, so just pass on the processUpdates
0605: * call to child components and facets that might be input components.
0606: * <p>
0607: * Components that were never rendered can't possibly be receiving
0608: * update data (no corresponding fields were ever put into the response)
0609: * so if this component is not rendered then this method does not
0610: * invoke processUpdates on its children.
0611: */
0612: public void processUpdates(FacesContext context) {
0613: if (context == null)
0614: throw new NullPointerException("context");
0615: if (!isRendered())
0616: return;
0617:
0618: for (Iterator<UIComponent> it = getFacetsAndChildren(); it
0619: .hasNext();) {
0620: it.next().processUpdates(context);
0621: }
0622: }
0623:
0624: public Object processSaveState(FacesContext context) {
0625: if (context == null)
0626: throw new NullPointerException("context");
0627: if (isTransient())
0628: return null;
0629: Map<String, Object> facetMap = null;
0630: int facetCount = getFacetCount();
0631: if (facetCount > 0) {
0632: for (Iterator<Entry<String, UIComponent>> it = getFacets()
0633: .entrySet().iterator(); it.hasNext();) {
0634: Entry<String, UIComponent> entry = it.next();
0635: UIComponent component = entry.getValue();
0636: if (!component.isTransient()) {
0637: if (facetMap == null)
0638: facetMap = new HashMap<String, Object>(
0639: facetCount, 1);
0640: facetMap.put(entry.getKey(), component
0641: .processSaveState(context));
0642: }
0643: }
0644: }
0645: List<Object> childrenList = null;
0646: int childCount = getChildCount();
0647: if (childCount > 0) {
0648: for (Iterator it = getChildren().iterator(); it.hasNext();) {
0649: UIComponent child = (UIComponent) it.next();
0650: if (!child.isTransient()) {
0651: if (childrenList == null) {
0652: childrenList = new ArrayList<Object>(childCount);
0653: }
0654: Object childState = child.processSaveState(context);
0655: if (childState != null) {
0656: childrenList.add(childState);
0657: }
0658: }
0659: }
0660: }
0661: return new Object[] { saveState(context), facetMap,
0662: childrenList };
0663: }
0664:
0665: public void processRestoreState(FacesContext context, Object state) {
0666: if (context == null)
0667: throw new NullPointerException("context");
0668: Object[] stateValues = (Object[]) state;
0669: Object myState = stateValues[0];
0670: Map<String, Object> facetMap = (Map<String, Object>) stateValues[1];
0671: List<Object> childrenList = (List<Object>) stateValues[2];
0672: if (facetMap != null && getFacetCount() > 0) {
0673: for (Iterator<Entry<String, UIComponent>> it = getFacets()
0674: .entrySet().iterator(); it.hasNext();) {
0675: Entry<String, UIComponent> entry = it.next();
0676: Object facetState = facetMap.get(entry.getKey());
0677: if (facetState != null) {
0678: entry.getValue().processRestoreState(context,
0679: facetState);
0680: } else {
0681: context.getExternalContext().log(
0682: "No state found to restore facet "
0683: + entry.getKey());
0684: }
0685: }
0686: }
0687: if (childrenList != null && getChildCount() > 0) {
0688: int idx = 0;
0689: for (Iterator<UIComponent> it = getChildren().iterator(); it
0690: .hasNext();) {
0691: UIComponent child = it.next();
0692: if (!child.isTransient()) {
0693: Object childState = childrenList.get(idx++);
0694: if (childState != null) {
0695: child.processRestoreState(context, childState);
0696: } else {
0697: context.getExternalContext().log(
0698: "No state found to restore child of component "
0699: + getId());
0700: }
0701: }
0702: }
0703: }
0704: restoreState(context, myState);
0705: }
0706:
0707: protected FacesContext getFacesContext() {
0708: return FacesContext.getCurrentInstance();
0709: }
0710:
0711: protected Renderer getRenderer(FacesContext context) {
0712: if (context == null)
0713: throw new NullPointerException("context");
0714: String rendererType = getRendererType();
0715: if (rendererType == null)
0716: return null;
0717: String renderKitId = context.getViewRoot().getRenderKitId();
0718: RenderKitFactory rkf = (RenderKitFactory) FactoryFinder
0719: .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
0720: RenderKit renderKit = rkf.getRenderKit(context, renderKitId);
0721: Renderer renderer = renderKit.getRenderer(getFamily(),
0722: rendererType);
0723: if (renderer == null) {
0724: getFacesContext().getExternalContext().log(
0725: "No Renderer found for component "
0726: + getPathToComponent(this )
0727: + " (component-family=" + getFamily()
0728: + ", renderer-type=" + rendererType + ")");
0729: log.warn("No Renderer found for component "
0730: + getPathToComponent(this ) + " (component-family="
0731: + getFamily() + ", renderer-type=" + rendererType
0732: + ")");
0733: }
0734: return renderer;
0735: }
0736:
0737: private String getPathToComponent(UIComponent component) {
0738: StringBuffer buf = new StringBuffer();
0739:
0740: if (component == null) {
0741: buf.append("{Component-Path : ");
0742: buf.append("[null]}");
0743: return buf.toString();
0744: }
0745:
0746: getPathToComponent(component, buf);
0747:
0748: buf.insert(0, "{Component-Path : ");
0749: buf.append("}");
0750:
0751: return buf.toString();
0752: }
0753:
0754: private void getPathToComponent(UIComponent component,
0755: StringBuffer buf) {
0756: if (component == null)
0757: return;
0758:
0759: StringBuffer intBuf = new StringBuffer();
0760:
0761: intBuf.append("[Class: ");
0762: intBuf.append(component.getClass().getName());
0763: if (component instanceof UIViewRoot) {
0764: intBuf.append(",ViewId: ");
0765: intBuf.append(((UIViewRoot) component).getViewId());
0766: } else {
0767: intBuf.append(",Id: ");
0768: intBuf.append(component.getId());
0769: }
0770: intBuf.append("]");
0771:
0772: buf.insert(0, intBuf.toString());
0773:
0774: getPathToComponent(component.getParent(), buf);
0775: }
0776:
0777: public boolean isTransient() {
0778: return _transient;
0779: }
0780:
0781: public void setTransient(boolean transientFlag) {
0782: _transient = transientFlag;
0783: }
0784:
0785: /**
0786: * Serializes objects which are "attached" to this component but which are
0787: * not UIComponent children of it. Examples are validator and listener
0788: * objects. To be precise, it returns an object which implements
0789: * java.io.Serializable, and which when serialized will persist the
0790: * state of the provided object.
0791: * <p>
0792: * If the attachedObject is a List then every object in the list is saved
0793: * via a call to this method, and the returned wrapper object contains
0794: * a List object.
0795: * <p>
0796: * If the object implements StateHolder then the object's saveState is
0797: * called immediately, and a wrapper is returned which contains both
0798: * this saved state and the original class name. However in the case
0799: * where the StateHolder.isTransient method returns true, null is
0800: * returned instead.
0801: * <p>
0802: * If the object implements java.io.Serializable then the object is simply
0803: * returned immediately; standard java serialization will later be used
0804: * to store this object.
0805: * <p>
0806: * In all other cases, a wrapper is returned which simply stores the type
0807: * of the provided object. When deserialized, a default instance of that
0808: * type will be recreated.
0809: */
0810: public static Object saveAttachedState(FacesContext context,
0811: Object attachedObject) {
0812: if (attachedObject == null)
0813: return null;
0814: if (attachedObject instanceof List) {
0815: List<Object> lst = new ArrayList<Object>(
0816: ((List) attachedObject).size());
0817: for (Iterator it = ((List) attachedObject).iterator(); it
0818: .hasNext();) {
0819: lst.add(saveAttachedState(context, it.next()));
0820: }
0821: return new _AttachedListStateWrapper(lst);
0822: } else if (attachedObject instanceof StateHolder) {
0823: if (((StateHolder) attachedObject).isTransient()) {
0824: return null;
0825: }
0826:
0827: return new _AttachedStateWrapper(attachedObject.getClass(),
0828: ((StateHolder) attachedObject).saveState(context));
0829: } else if (attachedObject instanceof Serializable) {
0830: return attachedObject;
0831: } else {
0832: return new _AttachedStateWrapper(attachedObject.getClass(),
0833: null);
0834: }
0835: }
0836:
0837: public static Object restoreAttachedState(FacesContext context,
0838: Object stateObj) throws IllegalStateException {
0839: if (context == null)
0840: throw new NullPointerException("context");
0841: if (stateObj == null)
0842: return null;
0843: if (stateObj instanceof _AttachedListStateWrapper) {
0844: List<Object> lst = ((_AttachedListStateWrapper) stateObj)
0845: .getWrappedStateList();
0846: List<Object> restoredList = new ArrayList<Object>(lst
0847: .size());
0848: for (Iterator<Object> it = lst.iterator(); it.hasNext();) {
0849: restoredList.add(restoreAttachedState(context, it
0850: .next()));
0851: }
0852: return restoredList;
0853: } else if (stateObj instanceof _AttachedStateWrapper) {
0854: Class clazz = ((_AttachedStateWrapper) stateObj).getClazz();
0855: Object restoredObject;
0856: try {
0857: restoredObject = clazz.newInstance();
0858: } catch (InstantiationException e) {
0859: throw new RuntimeException(
0860: "Could not restore StateHolder of type "
0861: + clazz.getName()
0862: + " (missing no-args constructor?)", e);
0863: } catch (IllegalAccessException e) {
0864: throw new RuntimeException(e);
0865: }
0866: if (restoredObject instanceof StateHolder) {
0867: Object wrappedState = ((_AttachedStateWrapper) stateObj)
0868: .getWrappedStateObject();
0869: ((StateHolder) restoredObject).restoreState(context,
0870: wrappedState);
0871: }
0872: return restoredObject;
0873: } else {
0874: return stateObj;
0875: }
0876: }
0877:
0878: /**
0879: * Invoked after the render phase has completed, this method
0880: * returns an object which can be passed to the restoreState
0881: * of some other instance of UIComponentBase to reset that
0882: * object's state to the same values as this object currently
0883: * has.
0884: */
0885: public Object saveState(FacesContext context) {
0886: Object values[] = new Object[7];
0887: values[0] = _id;
0888: values[1] = _rendered;
0889: values[2] = _rendererType;
0890: values[3] = _clientId;
0891: values[4] = saveAttributesMap();
0892: values[5] = saveAttachedState(context, _facesListeners);
0893: values[6] = saveBindings(context);
0894: return values;
0895: }
0896:
0897: /**
0898: * Invoked in the "restore view" phase, this initialises this
0899: * object's members from the values saved previously into the
0900: * provided state object.
0901: * <p>
0902: * @param state is an object previously returned by
0903: * the saveState method of this class.
0904: */
0905: public void restoreState(FacesContext context, Object state) {
0906: Object values[] = (Object[]) state;
0907: _id = (String) values[0];
0908: _rendered = (Boolean) values[1];
0909: _rendererType = (String) values[2];
0910: _clientId = (String) values[3];
0911: restoreAttributesMap(values[4]);
0912: _facesListeners = (List<FacesListener>) restoreAttachedState(
0913: context, values[5]);
0914: restoreValueExpressionMap(context, values[6]);
0915: }
0916:
0917: private Object saveAttributesMap() {
0918: return _attributesMap != null ? _attributesMap
0919: .getUnderlyingMap() : null;
0920: }
0921:
0922: private void restoreAttributesMap(Object stateObj) {
0923: if (stateObj != null) {
0924: _attributesMap = new _ComponentAttributesMap(this ,
0925: (Map<Object, Object>) stateObj);
0926: } else {
0927: _attributesMap = null;
0928: }
0929: }
0930:
0931: private Object saveBindings(FacesContext context) {
0932: if (bindings != null) {
0933: HashMap<String, Object> stateMap = new HashMap<String, Object>(
0934: bindings.size(), 1);
0935: for (Iterator<Entry<String, ValueExpression>> it = bindings
0936: .entrySet().iterator(); it.hasNext();) {
0937: Entry<String, ValueExpression> entry = it.next();
0938: stateMap.put(entry.getKey(), saveAttachedState(context,
0939: entry.getValue()));
0940: }
0941: return stateMap;
0942: }
0943:
0944: return null;
0945: }
0946:
0947: private void restoreValueExpressionMap(FacesContext context,
0948: Object stateObj) {
0949: if (stateObj != null) {
0950: Map stateMap = (Map) stateObj;
0951: int initCapacity = (stateMap.size() * 4 + 3) / 3;
0952: bindings = new HashMap<String, ValueExpression>(
0953: initCapacity);
0954: for (Iterator it = stateMap.entrySet().iterator(); it
0955: .hasNext();) {
0956: Map.Entry entry = (Map.Entry) it.next();
0957: bindings.put((String) entry.getKey(),
0958: (ValueExpression) restoreAttachedState(context,
0959: entry.getValue()));
0960: }
0961: } else {
0962: bindings = null;
0963: }
0964: }
0965:
0966: /**
0967: * @param string the component id, that should be a vaild one.
0968: */
0969: private void isIdValid(String string) {
0970:
0971: //is there any component identifier ?
0972: if (string == null)
0973: return;
0974:
0975: //Component identifiers must obey the following syntax restrictions:
0976: //1. Must not be a zero-length String.
0977: if (string.length() == 0) {
0978: throw new IllegalArgumentException(
0979: "component identifier must not be a zero-length String");
0980: }
0981:
0982: //let's look at all chars inside of the ID if it is a valid ID!
0983: char[] chars = string.toCharArray();
0984:
0985: //2. First character must be a letter or an underscore ('_').
0986: if (!Character.isLetter(chars[0]) && chars[0] != '_') {
0987: throw new IllegalArgumentException(
0988: "component identifier's first character must be a letter or an underscore ('_')! But it is \""
0989: + chars[0] + "\"");
0990: }
0991: for (int i = 1; i < chars.length; i++) {
0992: //3. Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
0993: if (!Character.isDigit(chars[i])
0994: && !Character.isLetter(chars[i]) && chars[i] != '-'
0995: && chars[i] != '_') {
0996: throw new IllegalArgumentException(
0997: "Subsequent characters of component identifier must be a letter, a digit, an underscore ('_'), or a dash ('-')! But component identifier contains \""
0998: + chars[i] + "\"");
0999: }
1000: }
1001: }
1002:
1003: <T> T getExpressionValue(String attribute, T explizitValue,
1004: T defaultValueIfExpressionNull) {
1005: return _ComponentUtils.getExpressionValue(this , attribute,
1006: explizitValue, defaultValueIfExpressionNull);
1007: }
1008:
1009: //------------------ GENERATED CODE BEGIN (do not modify!) --------------------
1010:
1011: private static final boolean DEFAULT_RENDERED = true;
1012:
1013: private Boolean _rendered = null;
1014: private String _rendererType = null;
1015:
1016: public void setRendered(boolean rendered) {
1017: _rendered = Boolean.valueOf(rendered);
1018: }
1019:
1020: public boolean isRendered() {
1021: return getExpressionValue("rendered", _rendered,
1022: DEFAULT_RENDERED);
1023: }
1024:
1025: public void setRendererType(String rendererType) {
1026: _rendererType = rendererType;
1027: }
1028:
1029: public String getRendererType() {
1030: return getExpressionValue("rendererType", _rendererType, null);
1031: }
1032:
1033: //------------------ GENERATED CODE END ---------------------------------------
1034:
1035: /**
1036: * @since 1.2
1037: */
1038:
1039: public int getFacetCount() {
1040: return _facetMap == null ? 0 : _facetMap.size();
1041: }
1042: }
|