All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.component.UIComponent Maven / Gradle / Ivy

There is a newer version: 4.1.0-RC2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package javax.faces.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;

import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Resource;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitHint;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ComponentSystemEventListener;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.event.PostRestoreStateEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.event.SystemEventListenerHolder;
import javax.faces.render.Renderer;
import javax.faces.render.RendererWrapper;

import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;

/**
 *
 * see Javadoc of J
 * SF Specification
 */
@JSFComponent(type = "javax.faces.Component", family = "javax.faces.Component",
              desc = "abstract base component", configExcluded = true)
public abstract class UIComponent
        implements PartialStateHolder, TransientStateHolder, SystemEventListenerHolder, ComponentSystemEventListener
{
    // TODO: Reorder methods, this class is a mess
    /**
     * Constant used in component attribute map to retrieve the BeanInfo of a composite
     * component.
     *
     * @see javax.faces.view.ViewDeclarationLanguage#getComponentMetadata(FacesContext, Resource)
     * @see javax.faces.view.ViewDeclarationLanguage#retargetAttachedObjects(FacesContext, UIComponent, List)
     * @see javax.faces.view.ViewDeclarationLanguage#retargetMethodExpressions(FacesContext, UIComponent)
     * @see javax.faces.application.Application#createComponent(FacesContext, Resource)
     */
    public static final String BEANINFO_KEY = "javax.faces.component.BEANINFO_KEY";

    /**
     * Constant used in BeanInfo descriptor as a key for retrieve an alternate component type
     * for create the composite base component. 
     *
     * @see javax.faces.application.Application#createComponent(FacesContext, Resource)
     */
    public static final String COMPOSITE_COMPONENT_TYPE_KEY = "javax.faces.component.COMPOSITE_COMPONENT_TYPE";

    /**
     * Constant used to define the facet inside this component that store the component hierarchy
     * generated by a composite component implementation, and then rendered. In other words, 
     * note that direct children of a component are not rendered, instead components inside 
     * this face are rendered.
     */
    public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME";

    /**
     * Constant used to store the current component that is being processed.
     *
     * @see #pushComponentToEL(FacesContext, UIComponent)
     * @see #popComponentFromEL(FacesContext)
     */
    public static final String CURRENT_COMPONENT = "javax.faces.component.CURRENT_COMPONENT";

    /**
     * Constant used to store the current composite component that is being processed. 
     *
     * @see #pushComponentToEL(FacesContext, UIComponent)
     * @see #popComponentFromEL(FacesContext)
     */
    public static final String CURRENT_COMPOSITE_COMPONENT = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT";

    /**
     * This constant has two usages. The first one is in component attribute map to identify the 
     * facet name under this component is child of its parent. The second one is on BeanInfo descriptor
     * as a key for a Map<String, PropertyDescriptor> that contains metadata information defined
     * by composite:facet tag and composite:implementation(because this one fills the facet referenced
     * by COMPOSITE_FACET_NAME constant). 
     */
    public static final String FACETS_KEY = "javax.faces.component.FACETS_KEY";

    /**
     * Constant used in component attribute map to store the {@link javax.faces.view.Location} object
     * where the definition of this component is.
     */
    public static final String VIEW_LOCATION_KEY = "javax.faces.component.VIEW_LOCATION_KEY";

    public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES
            = "javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES";

    /**
     * Indicate if the facesContext attribute values under the keys javax.faces.component.CURRENT_COMPONENT and
     * javax.faces.component.CURRENT_COMPOSITE_COMPONENT should be valid or not. By default, those keys are
     * deprecated since 2.1
     */
    @JSFWebConfigParam(since = "2.1.0", expectedValues = "true, false", defaultValue = "false")
    public static final String HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME
            = "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES";

    /**
     * The key under which the component stack is stored in the FacesContext.
     * ATTENTION: this constant is duplicate in CompositeComponentExpressionUtils.
     */
    private static final String _COMPONENT_STACK = "componentStack:" + UIComponent.class.getName();

    private static final String _CURRENT_COMPOSITE_COMPONENT_KEY = "compositeComponent:" + UIComponent.class.getName();

    Map, List> _systemEventListenerClassMap;

    /**
     * @deprecated
     */
    @Deprecated
    protected Map bindings;
    /**
     * Used to cache the map created using getResourceBundleMap() method, since this method could be called several
     * times when rendering the composite component. This attribute may not be serialized, so transient is used (There
     * are some very few special cases when UIComponent instances are serializable like t:schedule, so it is better if
     * transient is used).
     */
    private transient Map _resourceBundleMap = null;
    private boolean _inView = false;
    private _DeltaStateHelper _stateHelper = null;

    /**
     * In JSF 2.0 bindings map was deprecated, and replaced with a map
     * inside stateHelper. We need this one here because stateHelper needs
     * to be implemented from here and internally it depends from this property.
     */
    private boolean _initialStateMarked = false;

    /** Value of the {@link UIComponent#HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} parameter */
    private Boolean _honorCurrentComponentAttributes;

    public UIComponent()
    {
    }

    public abstract Map getAttributes();
    
    /**
     * @since 2.2
     * @return 
     */
    public final Map getPassThroughAttributes()
    {
        return getPassThroughAttributes(true);
    }
    
    /**
     * @since 2.2
     * @param create
     * @return 
     */
    public abstract Map getPassThroughAttributes(boolean create);

    /**
     *
     * {@inheritDoc}
     *
     * @since 2.0
     */
    public boolean initialStateMarked()
    {
        return _initialStateMarked;
    }

    /**
     * Invokes the invokeContextCallback method with the component, specified by clientId.
     *
     * @param context
     *            FacesContext for the current request
     * @param clientId
     *            the id of the desired UIComponent clazz
     * @param callback
     *            Implementation of the ContextCallback to be called
     * @return has component been found ?
     * @throws javax.faces.FacesException
     */
    public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
            throws FacesException
    {
        // java.lang.NullPointerException - if any of the arguments are null
        if (context == null || clientId == null || callback == null)
        {
            throw new NullPointerException();
        }

        pushComponentToEL(context, this);
        try
        {
            // searching for this component?
            boolean found = clientId.equals(this.getClientId(context));
            if (found)
            {
                try
                {
                    callback.invokeContextCallback(context, this);
                }
                catch (Exception e)
                {
                    throw new FacesException(e);
                }
                return found;
            }
            // Searching for this component's children/facets
            // [perf] Use getFacetsAndChildren() is nicer but this one prevents
            // create 1 iterator per component class that does not have
            // facets attached, which is a common case. 
            if (this.getFacetCount() > 0)
            {
                for (Iterator it = this.getFacets().values().iterator(); !found && it.hasNext(); )
                {
                    found = it.next().invokeOnComponent(context, clientId, callback);
                }                
            }
            if (this.getChildCount() > 0)
            {
                for (int i = 0, childCount = getChildCount(); !found && (i < childCount); i++)
                {
                    UIComponent child = getChildren().get(i);
                    found = child.invokeOnComponent(context, clientId, callback);
                }
            }
            return found;
        }
        finally
        {
            //all components must call popComponentFromEl after visiting is finished
            popComponentFromEL(context);
        }
    }

    /**
     *
     * @param component
     * @return true if the component is a composite component otherwise false is returned
     *
     *
     * @throws NullPointerException if the component is null
     * @since 2.0
     */
    public static boolean isCompositeComponent(UIComponent component)
    {

        //since _isCompositeComponent does it the same way we do it here also although I
        //would prefer following method

        //return component.getRendererType().equals("javax.faces.Composite");

        return component.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY);
    }

    /**
     * Indicate if this component is inside a view,
     * or in other words is contained by an UIViewRoot
     * instance (which represents the view). If this component
     * is a UIViewRoot instance, the components "always"
     * is on the view.
     *
     * By default it is false but for UIViewRoot instances is
     * true. 
     *
     * @return
     *
     * @since 2.0
     */
    public boolean isInView()
    {
        return _inView;
    }

    public abstract boolean isRendered();

    public void markInitialState()
    {
        _initialStateMarked = true;
    }

    /**
     *
     * This method indicates if a component is visitable
     * according to the hints passed by the VisitContext parameter!
     *
     * This method internally is used by visitTree and if it returns false
     * it short circuits the visitTree execution.
     *
     *
     *
     * @param context
     * @return
     *
     * @since 2.0
     */
    protected boolean isVisitable(VisitContext context)
    {

        Collection hints = context.getHints();

        if (hints.contains(VisitHint.SKIP_TRANSIENT) && this.isTransient())
        {
            return false;
        }

        if (hints.contains(VisitHint.SKIP_UNRENDERED) && !this.isRendered())
        {
            return false;
        }

        //executable cannot be handled here because we do not have any method to determine
        //whether a component is executable or not, this seems to be a hole in the spec!
        //but we can resolve it on ppr context level, where it is needed!
        //maybe in the long run we can move it down here, if it makes sense

        return true;
    }

    /**
     * @deprecated Replaced by setValueExpression
     */
    public abstract void setValueBinding(String name, ValueBinding binding);

    public void setValueExpression(String name, ValueExpression expression)
    {
        if (name == null)
        {
            throw new NullPointerException("name");
        }
        if (name.equals("id"))
        {
            throw new IllegalArgumentException("Can't set a ValueExpression for the 'id' property.");
        }
        if (name.equals("parent"))
        {
            throw new IllegalArgumentException("Can't set a ValueExpression for the 'parent' property.");
        }

        if (expression == null)
        {
            //if (bindings != null) {
            //    bindings.remove(name);
            //    if (bindings.isEmpty()) {
            //        bindings = null;
            //    }
            //}
            getStateHelper().remove(PropertyKeys.bindings, name);
        }
        else
        {
            if (expression.isLiteralText())
            {
                try
                {
                    Object value = expression.getValue(getFacesContext().getELContext());
                    getAttributes().put(name, value);
                    return;
                }
                catch (ELException e)
                {
                    throw new FacesException(e);
                }
            }

            //if (bindings == null) {
            //    bindings = new HashMap();
            //}
            //
            //bindings.put(name, expression);
            getStateHelper().put(PropertyKeys.bindings, name, expression);
        }
    }

    public String getClientId()
    {
        return getClientId(getFacesContext());
    }

    public abstract String getClientId(FacesContext context);

    /**
     * search for the nearest parent composite component, if no parent is found
     * it has to return null!
     *
     * if the component itself is null we have to return null as well!
     *
     * @param component the component to start from
     * @return the parent composite component if found otherwise null
     *
     * @since 2.0
     */
    public static UIComponent getCompositeComponentParent(UIComponent component)
    {

        if (component == null)
        {
            return null;
        }
        UIComponent parent = component;

        do
        {
            parent = parent.getParent();
            if (parent != null && UIComponent.isCompositeComponent(parent))
            {
                return parent;
            }
        } while (parent != null);
        return null;
    }

    /**
     * @since 1.2
     */
    public String getContainerClientId(FacesContext ctx)
    {
        if (ctx == null)
        {
            throw new NullPointerException("FacesContext ctx");
        }

        return getClientId(ctx);
    }

    /**
     *
     * @param context
     * @return
     *
     * @since 2.0
     */
    public static UIComponent getCurrentComponent(FacesContext context)
    {
        Boolean honorCurrentComponentAttributes = null;

        if (context.getViewRoot() != null)
        {
            honorCurrentComponentAttributes = ((UIComponent)context.getViewRoot())._honorCurrentComponentAttributes;
            if (honorCurrentComponentAttributes == null)
            {
                honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
            }
        }
        else
        {
            honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
        }

        if (Boolean.TRUE.equals(honorCurrentComponentAttributes))
        {
            return (UIComponent) context.getAttributes().get(UIComponent.CURRENT_COMPONENT);
        }
        else
        {
            List componentStack
                    = (List) context.getAttributes().get(UIComponent._COMPONENT_STACK);
            if (componentStack == null)
            {
                return null;
            }
            else
            {
                if (componentStack.size() > 0)
                {
                    return componentStack.get(componentStack.size()-1);
                }
                else
                {
                    return null;
                }
            }
        }
    }

    /**
     *
     * @param context
     * @return
     *
     * @since 2.0
     */
    public static UIComponent getCurrentCompositeComponent(FacesContext context)
    {
        Boolean honorCurrentComponentAttributes = null;

        if (context.getViewRoot() != null)
        {
            honorCurrentComponentAttributes = ((UIComponent)context.getViewRoot())._honorCurrentComponentAttributes;
            if (honorCurrentComponentAttributes == null)
            {
                honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
            }
        }
        else
        {
            honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
        }

        if (Boolean.TRUE.equals(honorCurrentComponentAttributes))
        {
            return (UIComponent) context.getAttributes().get(UIComponent.CURRENT_COMPOSITE_COMPONENT);
        }
        else
        {
            return (UIComponent) context.getAttributes().get(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY);
        }
    }

    public abstract String getFamily();

    public abstract String getId();

    public List getListenersForEventClass(Class eventClass)
    {
        List listeners;
        if (_systemEventListenerClassMap == null)
        {
            listeners = Collections.emptyList();
        }
        else
        {
            listeners = _systemEventListenerClassMap.get(eventClass);
            if (listeners == null)
            {
                listeners = Collections.emptyList();
            }
            else
            {
                listeners = Collections.unmodifiableList(listeners);
            }
        }

        return listeners;
    }

    /**
     *
     * @return
     *
     * @since 2.0
     */
    public UIComponent getNamingContainer()
    {
        // Starting with "this", return the closest component in the ancestry that is a NamingContainer 
        // or null if none can be found.
        UIComponent component = this;
        do
        {
            if (component instanceof NamingContainer)
            {
                return component;
            }

            component = component.getParent();
        } while (component != null);

        return null;
    }

    public abstract void setId(String id);

    /**
     * Define if the component is on the view or not.
     * 

* This value is set in the following conditions: *

*
    *
  • Component / Facet added: if the parent isInView = true, * set it to true and all their children or facets, * otherwise take no action
  • *
  • Component / Facet removed: if the parent isInView = false, * set it to false and all their children or facets, * otherwise take no action
  • *
      * @param isInView * * @since 2.0 */ public void setInView(boolean isInView) { _inView = isInView; } /** * For JSF-framework internal use only. Don't call this method to add components to the component tree. Use * parent.getChildren().add(child) instead. */ public abstract void setParent(UIComponent parent); /** * Returns the parent of the component. Children can be added to or removed from a component even if this method * returns null for the child. */ public abstract UIComponent getParent(); public abstract void setRendered(boolean rendered); public abstract String getRendererType(); public abstract void setRendererType(String rendererType); public abstract boolean getRendersChildren(); public Map getResourceBundleMap() { if (_resourceBundleMap == null) { FacesContext context = getFacesContext(); Locale locale = context.getViewRoot().getLocale(); ClassLoader loader = _ClassUtils.getContextClassLoader(); try { // looks for a ResourceBundle with a base name equal to the fully qualified class // name of the current UIComponent this and Locale equal to the Locale of the current UIViewRoot. _resourceBundleMap = new BundleMap(ResourceBundle.getBundle(getClass().getName(), locale, loader)); } catch (MissingResourceException e) { // If no such bundle is found, and the component is a composite component if (this._isCompositeComponent()) { // No need to check componentResource (the resource used to build the composite // component instance) to null since it is already done on this._isCompositeComponent() Resource componentResource = (Resource) getAttributes().get(Resource.COMPONENT_RESOURCE_KEY); // Let resourceName be the resourceName of the Resource for this composite component, // replacing the file extension with ".properties" int extensionIndex = componentResource.getResourceName().lastIndexOf('.'); String resourceName = (extensionIndex < 0 ? componentResource.getResourceName() : componentResource.getResourceName().substring(0, extensionIndex)) + ".properties"; // Let libraryName be the libraryName of the the Resource for this composite component. // Call ResourceHandler.createResource(java.lang.String,java.lang.String), passing the derived // resourceName and // libraryName. Resource bundleResource = context.getApplication().getResourceHandler() .createResource(resourceName, componentResource.getLibraryName()); if (bundleResource != null) { // If the resultant Resource exists and can be found, the InputStream for the resource // is used to create a ResourceBundle. If either of the two previous steps for obtaining the // ResourceBundle // for this component is successful, the ResourceBundle is wrapped in a Map and // returned. try { _resourceBundleMap = new BundleMap(new PropertyResourceBundle(bundleResource.getInputStream())); } catch (IOException e1) { // Nothing happens, then resourceBundleMap is set as empty map } } } // Otherwise Collections.EMPTY_MAP is returned. if (_resourceBundleMap == null) { _resourceBundleMap = Collections.emptyMap(); } } } return _resourceBundleMap; } /** * @deprecated Replaced by getValueExpression */ public abstract ValueBinding getValueBinding(String name); public ValueExpression getValueExpression(String name) { if (name == null) { throw new NullPointerException("name can not be null"); } Map bindings = (Map) getStateHelper(). get(PropertyKeys.bindings); if (bindings == null) { if (!(this instanceof UIComponentBase)) { // if the component does not inherit from UIComponentBase and don't implements JSF 1.2 or later ValueBinding vb = getValueBinding(name); if (vb != null) { //bindings = new HashMap(); ValueExpression ve = new _ValueBindingToValueExpression(vb); getStateHelper().put(PropertyKeys.bindings, name, ve); return ve; } } } else { //return bindings.get(name); return (ValueExpression) bindings.get(name); } return null; } public abstract List getChildren(); public abstract int getChildCount(); public abstract UIComponent findComponent(String expr); public abstract Map getFacets(); public abstract UIComponent getFacet(String name); public abstract Iterator getFacetsAndChildren(); public abstract void broadcast(FacesEvent event) throws AbortProcessingException; /** * {@inheritDoc} * * @since 2.0 */ public void clearInitialState() { _initialStateMarked = false; } public abstract void decode(FacesContext context); public abstract void encodeBegin(FacesContext context) throws IOException; public abstract void encodeChildren(FacesContext context) throws IOException; public abstract void encodeEnd(FacesContext context) throws IOException; public void encodeAll(FacesContext context) throws IOException { if (context == null) { throw new NullPointerException(); } pushComponentToEL(context, this); try { if (!isRendered()) { return; } } finally { popComponentFromEL(context); } //if (isRendered()) { this.encodeBegin(context); // rendering children if (this.getRendersChildren()) { this.encodeChildren(context); } // let children render itself else { if (this.getChildCount() > 0) { for (int i = 0; i < this.getChildCount(); i++) { UIComponent comp = this.getChildren().get(i); comp.encodeAll(context); } } } this.encodeEnd(context); //} } protected abstract void addFacesListener(FacesListener listener); protected abstract FacesListener[] getFacesListeners(Class clazz); protected abstract void removeFacesListener(FacesListener listener); public abstract void queueEvent(FacesEvent event); public abstract void processRestoreState(FacesContext context, Object state); public abstract void processDecodes(FacesContext context); public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { // The default implementation performs the following action. If the argument event is an instance of // AfterRestoreStateEvent, if (event instanceof PostRestoreStateEvent) { // call this.getValueExpression(java.lang.String) passing the literal string "binding" ValueExpression expression = getValueExpression("binding"); // If the result is non-null, set the value of the ValueExpression to be this. if (expression != null) { expression.setValue(getFacesContext().getELContext(), this); } //we issue a PostRestoreStateEvent //we issue it here because the spec clearly states what UIComponent is allowed to do //the main issue is that the spec does not say anything about a global dispatch on this level //but a quick blackbox test against the ri revealed that the event clearly is dispatched //at restore level for every component so we either issue it here or in UIViewRoot and/or the facelet // and jsp restore state triggers, a central point is preferrble so we do it here //TODO ask the EG the spec clearly contradicts blackbox RI behavior here //getFacesContext().getApplication().publishEvent(getFacesContext(), // PostRestoreStateEvent.class, UIComponent.class, this); // JSF 2.2 vdl.createComponent() requires special handling to refresh // dynamic parts when refreshing is done. The only way to do it is // attaching a listener to PostRestoreStateEvent, so we need to do this // invocation here. // Do it inside UIComponent.processEvent() is better because in facelets // UILeaf we can skip this part just overriding the method. List listeners = this.getListenersForEventClass( PostRestoreStateEvent.class); if (!listeners.isEmpty()) { for (int i = 0, size = listeners.size(); i < size; i++) { SystemEventListener listener = listeners.get(i); if (listener.isListenerForSource(this)) { // Check if the listener points again to the component, to // avoid StackoverflowException boolean shouldProcessEvent = true; if (listener instanceof EventListenerWrapper && ((EventListenerWrapper)listener).listenerCapability == EventListenerWrapper.LISTENER_TYPE_COMPONENT) { shouldProcessEvent = false; } if (shouldProcessEvent) { listener.processEvent(event); } } } } } } public abstract void processValidators(FacesContext context); public abstract void processUpdates(FacesContext context); public abstract java.lang.Object processSaveState(FacesContext context); public void subscribeToEvent(Class eventClass, ComponentSystemEventListener componentListener) { // The default implementation creates an inner SystemEventListener instance that wraps argument // componentListener as the listener argument. if (eventClass == null) { throw new NullPointerException("eventClass required"); } if (componentListener == null) { throw new NullPointerException("componentListener required"); } SystemEventListener listener = new EventListenerWrapper(this, componentListener); // Make sure the map exists if (_systemEventListenerClassMap == null) { _systemEventListenerClassMap = new HashMap, List>(); } List listeners = _systemEventListenerClassMap.get(eventClass); // Make sure the list for class exists if (listeners == null) { // how many listeners per event type can single component have? // We use 3 here as expected number, but it is a question listeners = new _DeltaList(new ArrayList(3)); _systemEventListenerClassMap.put(eventClass, listeners); } // Deal with contains? Spec is silent listeners.add(listener); } public void unsubscribeFromEvent(Class eventClass, ComponentSystemEventListener componentListener) { /* * When doing the comparison to determine if an existing listener is equal to the argument componentListener * (and thus must be removed), the equals() method on the existing listener must be invoked, passing the * argument componentListener, rather than the other way around. * * -=Simon Lessard=- What is that supposed to mean? Are we supposed to keep * an internal map of created listener wrappers? * -= Leonardo Uribe=- Yes, it is supposed a wrapper should be used to hold listener references, to prevent * serialize component instances on the state. */ if (eventClass == null) { throw new NullPointerException("eventClass required"); } if (componentListener == null) { throw new NullPointerException("componentListener required"); } if (_systemEventListenerClassMap != null) { List listeners = _systemEventListenerClassMap.get(eventClass); if (listeners != null && !listeners.isEmpty()) { for (Iterator it = listeners.iterator(); it.hasNext(); ) { ComponentSystemEventListener listener = ((EventListenerWrapper) it.next()).getComponentSystemEventListener(); if (listener != null && listener.equals(componentListener)) { it.remove(); break; } } } } } /** * The visit tree method, visit tree walks over a subtree and processes * the callback object to perform some operation on the subtree *

      * there are some details in the implementation which according to the spec have * to be in place: * a) before calling the callback and traversing into the subtree pushComponentToEL * has to be called * b) after the processing popComponentFromEL has to be performed to remove the component * from the el *

      *

      * The tree traversal optimizations are located in the visit context and can be replaced * via the VisitContextFactory in the faces-config factory section *

      * * @param context the visit context which handles the processing details * @param callback the callback to be performed * @return false if the processing is not done true if we can shortcut * the visiting because we are done with everything * * @since 2.0 */ public boolean visitTree(VisitContext context, VisitCallback callback) { try { pushComponentToEL(context.getFacesContext(), this); if (!isVisitable(context)) { return false; } VisitResult res = context.invokeVisitCallback(this, callback); switch (res) { //we are done nothing has to be processed anymore case COMPLETE: return true; case REJECT: return false; //accept default: if (getFacetCount() > 0) { for (UIComponent facet : getFacets().values()) { if (facet.visitTree(context, callback)) { return true; } } } int childCount = getChildCount(); if (childCount > 0) { for (int i = 0; i < childCount; i++) { UIComponent child = getChildren().get(i); if (child.visitTree(context, callback)) { return true; } } } return false; } } finally { //all components must call popComponentFromEl after visiting is finished popComponentFromEL(context.getFacesContext()); } } protected abstract FacesContext getFacesContext(); protected abstract Renderer getRenderer(FacesContext context); /** * Note that id, clientId properties * never change its value after the component is populated, * so we don't need to store it on StateHelper or restore it when * initialStateMarked == true * (Note that rendererType is suspicious, in theory this field is * initialized on constructor, but on 1.1 and 1.2 is saved and restored, * so to keep backward behavior we put it on StateHelper ) * * Also, facesListeners can't be wrapped on StateHelper because it * needs to handle PartialStateHolder instances when it is saved and * restored and this interface does not implement PartialStateHolder, * so we can't propagate calls to markInitialState and clearInitialState, * in other words, the List wrapped by StateHelper does not handle * PartialStateHolder items. * * "bindings" map does not need to deal with PartialStateHolder instances, * so we can use StateHelper feature (handle delta for this map or in * other words track add/removal from bindings map as delta). */ enum PropertyKeys { rendered, rendererType, attributesMap, bindings, facesListeners, passThroughAttributesMap } protected StateHelper getStateHelper() { return getStateHelper(true); } /** * returns a delta state saving enabled state helper * for the current component * @param create if true a state helper is created if not already existing * @return an implementation of the StateHelper interface or null if none exists and create is set to false */ protected StateHelper getStateHelper(boolean create) { if (_stateHelper != null) { return _stateHelper; } if (create) { _stateHelper = new _DeltaStateHelper(this); } return _stateHelper; } public final TransientStateHelper getTransientStateHelper() { return getTransientStateHelper(true); } public TransientStateHelper getTransientStateHelper(boolean create) { if (_stateHelper != null) { return _stateHelper; } if (create) { _stateHelper = new _DeltaStateHelper(this); } return _stateHelper; } public void restoreTransientState(FacesContext context, Object state) { getTransientStateHelper().restoreTransientState(context, state); } public Object saveTransientState(FacesContext context) { return getTransientStateHelper().saveTransientState(context); } @SuppressWarnings("unchecked") public final void popComponentFromEL(FacesContext context) { Map contextAttributes = context.getAttributes(); if (_honorCurrentComponentAttributes == null) { _honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context); } if (Boolean.TRUE.equals(_honorCurrentComponentAttributes)) { // Pop the current UIComponent from the FacesContext attributes map so that the previous // UIComponent, if any, becomes the current component. List componentStack = (List) contextAttributes.get(UIComponent._COMPONENT_STACK); UIComponent oldCurrent = (UIComponent) contextAttributes.get(UIComponent.CURRENT_COMPONENT); UIComponent newCurrent = null; if (componentStack != null && !componentStack.isEmpty()) { if (!this.equals(oldCurrent)) { //Check on the componentStack if it can be found int componentIndex = componentStack.lastIndexOf(this); if (componentIndex >= 0) { //for (int i = 0; i < (componentIndex + 1); i++) for (int i = componentStack.size()-1; i >= componentIndex ; i--) { newCurrent = componentStack.remove(componentStack.size()-1); } } else { //Component not found on the stack. Do not pop. return; } } else { newCurrent = componentStack.remove(componentStack.size()-1); } } else { //Reset the current composite component contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, null); } oldCurrent = (UIComponent) contextAttributes.put(UIComponent.CURRENT_COMPONENT, newCurrent); if (oldCurrent != null && oldCurrent._isCompositeComponent() && newCurrent != null) { // Recalculate the current composite component if (newCurrent._isCompositeComponent()) { contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, newCurrent); } else { UIComponent previousCompositeComponent = null; for (int i = componentStack.size()-1; i >= 0; i--) { UIComponent component = componentStack.get(i); if (component._isCompositeComponent()) { previousCompositeComponent = component; break; } } contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, previousCompositeComponent); } } } else { // Pop the current UIComponent from the FacesContext attributes map so that the previous // UIComponent, if any, becomes the current component. List componentStack = (List) contextAttributes.get(UIComponent._COMPONENT_STACK); UIComponent oldCurrent = null; if (componentStack != null && !componentStack.isEmpty()) { int componentIndex = componentStack.lastIndexOf(this); if (componentIndex >= 0) { for (int i = componentStack.size()-1; i >= componentIndex ; i--) { oldCurrent = componentStack.remove(componentStack.size()-1); } } else { return; } } if (oldCurrent != null && oldCurrent._isCompositeComponent()) { // Recalculate the current composite component UIComponent previousCompositeComponent = null; for (int i = componentStack.size()-1; i >= 0; i--) { UIComponent component = componentStack.get(i); if (component._isCompositeComponent()) { previousCompositeComponent = component; break; } } contextAttributes.put(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY, previousCompositeComponent); } } } @SuppressWarnings("unchecked") public final void pushComponentToEL(FacesContext context, UIComponent component) { if (component == null) { component = this; } Map contextAttributes = context.getAttributes(); if (_honorCurrentComponentAttributes == null) { _honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context); } if (Boolean.TRUE.equals(_honorCurrentComponentAttributes)) { UIComponent currentComponent = (UIComponent) contextAttributes.get(UIComponent.CURRENT_COMPONENT); if (currentComponent != null) { List componentStack = (List) contextAttributes.get(UIComponent._COMPONENT_STACK); if (componentStack == null) { componentStack = new ArrayList(); contextAttributes.put(UIComponent._COMPONENT_STACK, componentStack); } componentStack.add(currentComponent); } // Push the current UIComponent this to the FacesContext attribute map using the key CURRENT_COMPONENT // saving the previous UIComponent associated with CURRENT_COMPONENT for a subsequent call to // popComponentFromEL(javax.faces.context.FacesContext). contextAttributes.put(UIComponent.CURRENT_COMPONENT, component); if (component._isCompositeComponent()) { contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, component); } } else { List componentStack = (List) contextAttributes.get(UIComponent._COMPONENT_STACK); if (componentStack == null) { componentStack = new ArrayList(); contextAttributes.put(UIComponent._COMPONENT_STACK, componentStack); } componentStack.add(component); if (component._isCompositeComponent()) { contextAttributes.put(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY, component); } } } /** * @since 1.2 */ public int getFacetCount() { // not sure why the RI has this method in both // UIComponent and UIComponentBase Map facets = getFacets(); return facets == null ? 0 : facets.size(); } private boolean _isCompositeComponent() { //moved to the static method return UIComponent.isCompositeComponent(this); } boolean isCachedFacesContext() { return false; } // Dummy method to prevent cast for UIComponentBase when caching void setCachedFacesContext(FacesContext facesContext) { } /** * Gets value of "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES" parameter cached in facesContext.attributes * or resolves that param and caches its value in facesContext.attributes. * * @return canonical Boolean value for parameter "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES" */ private static Boolean _getHonorCurrentComponentAttributes(FacesContext facesContext) { // performance note: we cache value in facesContext.attributes because // 1) methods pushComponentToEL, popComponentFromEl, getCurrentComponent a getCurrentCompositeComponent // can use that value // 2) getExternalContext().getInitParameter has undetermined performance. In typical JSF app, there // are one or two wrappers around external context; servletContext.getInitParameter has also unknown // implementation and performance Map attributes = facesContext.getAttributes(); Boolean paramValue = (Boolean) attributes.get(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME); if (paramValue == null) { String param = facesContext.getExternalContext().getInitParameter(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME); paramValue = Boolean.valueOf((param != null && Boolean.valueOf(param).booleanValue())); attributes.put(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME, paramValue); } return paramValue; } private static class BundleMap implements Map { private ResourceBundle _bundle; private List _values; public BundleMap(ResourceBundle bundle) { _bundle = bundle; } // Optimized methods public String get(Object key) { try { return (String) _bundle.getObject(key.toString()); } catch (Exception e) { return "???" + key + "???"; } } public boolean isEmpty() { return !_bundle.getKeys().hasMoreElements(); } public boolean containsKey(Object key) { try { return _bundle.getObject(key.toString()) != null; } catch (MissingResourceException e) { return false; } } // Unoptimized methods public Collection values() { if (_values == null) { _values = new ArrayList(); for (Enumeration enumer = _bundle.getKeys(); enumer.hasMoreElements(); ) { String v = _bundle.getString(enumer.nextElement()); _values.add(v); } } return _values; } public int size() { return values().size(); } public boolean containsValue(Object value) { return values().contains(value); } public Set> entrySet() { Set> set = new HashSet>(); for (Enumeration enumer = _bundle.getKeys(); enumer.hasMoreElements(); ) { final String k = enumer.nextElement(); set.add(new Map.Entry() { public String getKey() { return k; } public String getValue() { return (String) _bundle.getObject(k); } public String setValue(String value) { throw new UnsupportedOperationException(); } }); } return set; } public Set keySet() { Set set = new HashSet(); for (Enumeration enumer = _bundle.getKeys(); enumer.hasMoreElements(); ) { set.add(enumer.nextElement()); } return set; } // Unsupported methods public String remove(Object key) { throw new UnsupportedOperationException(); } public void putAll(Map t) { throw new UnsupportedOperationException(); } public String put(String key, String value) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } } static class EventListenerWrapper implements SystemEventListener, PartialStateHolder { private Class componentClass; private ComponentSystemEventListener listener; private boolean _initialStateMarked; private int listenerCapability; private transient UIComponent _component; private static final int LISTENER_SAVE_STATE_HOLDER = 1; private static final int LISTENER_SAVE_PARTIAL_STATE_HOLDER = 2; private static final int LISTENER_TYPE_COMPONENT = 4; private static final int LISTENER_TYPE_RENDERER = 8; private static final int LISTENER_TYPE_OTHER = 16; public EventListenerWrapper() { //need a no-arg constructor for state saving purposes super(); } /** * Note we have two cases: * * 1. listener is an instance of UIComponent. In this case we cannot save and restore * it because we need to point to the real component, but we can assume the instance * is the same because UIComponent.subscribeToEvent says so. Also take into account * this case is the reason why we need a wrapper for UIComponent.subscribeToEvent * 2. listener is an instance of Renderer. In this case we can assume the same renderer * used by the source component is the one used by the listener (ListenerFor). * 3. listener is an instance of ComponentSystemEventListener but not from UIComponent. * In this case, the instance could implement StateHolder, PartialStateHolder or do * implement anything, so we have to deal with that case as usual. * * @param component * @param listener */ public EventListenerWrapper(UIComponent component, ComponentSystemEventListener listener) { assert component != null; assert listener != null; this.componentClass = component.getClass(); this.listener = listener; this._component = component; initListenerCapability(); } private void initListenerCapability() { this.listenerCapability = 0; if (this.listener instanceof UIComponent) { this.listenerCapability = LISTENER_TYPE_COMPONENT; } else if (this.listener instanceof Renderer) { this.listenerCapability = LISTENER_TYPE_RENDERER; } else { if (this.listener instanceof PartialStateHolder) { this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_PARTIAL_STATE_HOLDER; } else if (this.listener instanceof StateHolder) { this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_STATE_HOLDER; } else { this.listenerCapability = LISTENER_TYPE_OTHER; } } } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof EventListenerWrapper) { EventListenerWrapper other = (EventListenerWrapper) o; return componentClass.equals(other.componentClass) && listener.equals(other.listener); } else { return false; } } @Override public int hashCode() { return componentClass.hashCode() + listener.hashCode(); } public boolean isListenerForSource(Object source) { // and its implementation of SystemEventListener.isListenerForSource(java.lang.Object) must return true // if the instance class of this UIComponent is assignable from the argument to isListenerForSource. return source.getClass().isAssignableFrom(componentClass); } public ComponentSystemEventListener getComponentSystemEventListener() { return listener; } public void processEvent(SystemEvent event) { // This inner class must call through to the argument componentListener in its implementation of // SystemEventListener.processEvent(javax.faces.event.SystemEvent) assert event instanceof ComponentSystemEvent; listener.processEvent((ComponentSystemEvent) event); } public void clearInitialState() { //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder) if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) { ((PartialStateHolder) listener).clearInitialState(); } _initialStateMarked = false; } public boolean initialStateMarked() { //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder) if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) { return ((PartialStateHolder) listener).initialStateMarked(); } //return false; return _initialStateMarked; } public void markInitialState() { //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder) if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) { ((PartialStateHolder) listener).markInitialState(); } _initialStateMarked = true; } public boolean isTransient() { //if ( listener instanceof StateHolder) if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 || (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0) { return ((StateHolder) listener).isTransient(); } return false; } public void restoreState(FacesContext context, Object state) { if (state == null) { return; } Object[] values = (Object[]) state; componentClass = (Class) values[0]; if (values[1] instanceof _AttachedDeltaWrapper) { ((StateHolder) listener).restoreState(context, ((_AttachedDeltaWrapper) values[1]).getWrappedStateObject()); } else { //Full restore listenerCapability = (Integer) values[2]; _component = UIComponent.getCurrentComponent(context); if ((listenerCapability & LISTENER_TYPE_COMPONENT) != 0) { listener = _component; } else if ((listenerCapability & LISTENER_TYPE_RENDERER) != 0) { //listener = (ComponentSystemEventListener) // UIComponent.getCurrentComponent(context).getRenderer(context); Renderer renderer = _component.getRenderer(context); Integer i = (Integer) values[1]; if (i != null && i >= 0) { while (i > 0) { renderer = ((RendererWrapper) renderer).getWrapped(); i--; } } listener = (ComponentSystemEventListener) renderer; } else { listener = (ComponentSystemEventListener) UIComponentBase.restoreAttachedState(context, values[1]); } /* listener = values[1] == null ? UIComponent.getCurrentComponent(context) : (ComponentSystemEventListener) UIComponentBase.restoreAttachedState(context, values[1]); */ } } public Object saveState(FacesContext context) { if (!initialStateMarked()) { /* Object[] state = new Object[2]; state[0] = componentClass; if (!(listener instanceof UIComponent)) { state[1] = UIComponentBase.saveAttachedState(context, listener); } return state; */ Object[] state = new Object[3]; state[0] = componentClass; //If this is not a component or a renderer, save it calling UIComponent.saveAttachedState if (!((listenerCapability & LISTENER_TYPE_COMPONENT) != 0 || (listenerCapability & LISTENER_TYPE_RENDERER) != 0)) { state[1] = UIComponentBase.saveAttachedState(context, listener); } else { if ( (listenerCapability & LISTENER_TYPE_RENDERER) != 0) { UIComponent componentRef = _component != null ? _component : getCurrentComponent(context); Renderer renderer = componentRef.getRenderer(context); int i = 0; while (renderer != null && !renderer.getClass().equals(listener.getClass())) { if (renderer instanceof RendererWrapper) { renderer = ((RendererWrapper) renderer).getWrapped(); i++; } else { renderer = null; i = -1; } } if (i != -1) { // Store the number so we can get the right wrapper to invoke the method. state[1] = i; } else { state[1] = null; } } else { state[1] = null; } } state[2] = (Integer) listenerCapability; return state; } else { // If initialStateMarked() == true means two things: // 1. PSS is being used if ((listenerCapability & LISTENER_TYPE_COMPONENT) != 0) { return null; } else if ((listenerCapability & LISTENER_TYPE_RENDERER) != 0) { return null; } else { if ((listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0 || (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) { Object listenerSaved = ((StateHolder) listener).saveState(context); if (listenerSaved == null) { return null; } return new Object[]{componentClass, new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)}; } else { //This is not necessary, because the instance is considered serializable! return null; } } /* Object listenerSaved = ((StateHolder) listener).saveState(context); if (listenerSaved == null) { return null; } return new Object[]{componentClass, new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)}; */ } } public void setTransient(boolean newTransientValue) { if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 || (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0) { ((StateHolder) listener).setTransient(newTransientValue); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy