| /* |
| * 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 org.apache.myfaces.core.api.shared.ClassUtils; |
| 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.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 <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">J |
| * SF Specification</a> |
| */ |
| @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<Class<? extends SystemEvent>, List<SystemEventListener>> _systemEventListenerClassMap; |
| |
| /** |
| * 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<String, String> _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<String, Object> getAttributes(); |
| |
| /** |
| * @since 2.2 |
| * @return |
| */ |
| public Map<String,Object> getPassThroughAttributes() |
| { |
| return getPassThroughAttributes(true); |
| } |
| |
| /** |
| * @since 2.2 |
| * @param create |
| * @return A {@code Map} instance, or {@code null}. |
| */ |
| public Map<String,Object> getPassThroughAttributes(boolean create) |
| { |
| return Collections.emptyMap(); |
| } |
| |
| /** |
| * |
| * {@inheritDoc} |
| * |
| * @since 2.0 |
| */ |
| @Override |
| public boolean initialStateMarked() |
| { |
| return _initialStateMarked; |
| } |
| |
| /** |
| * Invokes the <code>invokeContextCallback</code> method with the component, specified by <code>clientId</code>. |
| * |
| * @param context |
| * <code>FacesContext</code> for the current request |
| * @param clientId |
| * the id of the desired <code>UIComponent</code> clazz |
| * @param callback |
| * Implementation of the <code>ContextCallback</code> 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<UIComponent> 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) |
| { |
| 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(); |
| |
| @Override |
| 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<VisitHint> 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; |
| } |
| |
| 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) |
| { |
| 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); |
| } |
| } |
| |
| 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<UIComponent> componentStack |
| = (List<UIComponent>) 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(); |
| |
| @Override |
| public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass) |
| { |
| List<SystemEventListener> 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. |
| * <p> |
| * This value is set in the following conditions: |
| * </p> |
| * <ul> |
| * <li>Component / Facet added: if the parent isInView = true, |
| * set it to true and all their children or facets, |
| * otherwise take no action</li> |
| * <li>Component / Facet removed: if the parent isInView = false, |
| * set it to false and all their children or facets, |
| * otherwise take no action</li> |
| * </ul> |
| * @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 |
| * <code>parent.getChildren().add(child)</code> 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<String, String> getResourceBundleMap() |
| { |
| if (_resourceBundleMap == null) |
| { |
| FacesContext context = getFacesContext(); |
| Locale locale = context.getViewRoot().getLocale(); |
| ClassLoader loader = ClassUtils.getContextClassLoader(); |
| |
| try |
| { |
| ResourceBundle.Control bundleControl = (ResourceBundle.Control) context.getExternalContext() |
| .getApplicationMap().get("org.apache.myfaces.RESOURCE_BUNDLE_CONTROL"); |
| |
| // 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. |
| if (bundleControl == null) |
| { |
| _resourceBundleMap = new BundleMap( |
| ResourceBundle.getBundle(getClass().getName(), locale, loader)); |
| } |
| else |
| { |
| _resourceBundleMap = new BundleMap( |
| ResourceBundle.getBundle(getClass().getName(), locale, loader, bundleControl)); |
| } |
| } |
| 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<String, String> 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; |
| } |
| |
| public ValueExpression getValueExpression(String name) |
| { |
| if (name == null) |
| { |
| throw new NullPointerException("name can not be null"); |
| } |
| |
| Map<String, Object> bindings = (Map<String, Object>) getStateHelper().get(PropertyKeys.bindings); |
| if (bindings != null) |
| { |
| return (ValueExpression) bindings.get(name); |
| } |
| |
| return null; |
| } |
| |
| public abstract List<UIComponent> getChildren(); |
| |
| public abstract int getChildCount(); |
| |
| public abstract UIComponent findComponent(String expr); |
| |
| public abstract Map<String, UIComponent> getFacets(); |
| |
| public abstract UIComponent getFacet(String name); |
| |
| public abstract Iterator<UIComponent> getFacetsAndChildren(); |
| |
| public abstract void broadcast(FacesEvent event) throws AbortProcessingException; |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @since 2.0 |
| */ |
| @Override |
| 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); |
| } |
| |
| 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); |
| |
| @Override |
| 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, 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<SystemEventListener> 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<? extends SystemEvent> 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<>(4, 1f); |
| } |
| |
| List<SystemEventListener> listeners = _systemEventListenerClassMap.get(eventClass); |
| if (listeners == null) |
| { |
| listeners = new _DeltaList<>(3); |
| _systemEventListenerClassMap.put(eventClass, listeners); |
| } |
| |
| // Deal with contains? Spec is silent |
| listeners.add(listener); |
| } |
| |
| public void unsubscribeFromEvent(Class<? extends SystemEvent> 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<SystemEventListener> listeners = _systemEventListenerClassMap.get(eventClass); |
| if (listeners != null && !listeners.isEmpty()) |
| { |
| for (Iterator<SystemEventListener> 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 |
| * <p> |
| * 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 |
| * </p> |
| * <p> |
| * The tree traversal optimizations are located in the visit context and can be replaced |
| * via the VisitContextFactory in the faces-config factory section |
| * </p> |
| * |
| * @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 TransientStateHelper getTransientStateHelper() |
| { |
| return getTransientStateHelper(true); |
| } |
| |
| public TransientStateHelper getTransientStateHelper(boolean create) |
| { |
| if (_stateHelper != null) |
| { |
| return _stateHelper; |
| } |
| if (create) |
| { |
| _stateHelper = new _DeltaStateHelper(this); |
| } |
| return _stateHelper; |
| } |
| |
| @Override |
| public void restoreTransientState(FacesContext context, Object state) |
| { |
| getTransientStateHelper().restoreTransientState(context, state); |
| } |
| |
| @Override |
| public Object saveTransientState(FacesContext context) |
| { |
| return getTransientStateHelper().saveTransientState(context); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public void popComponentFromEL(FacesContext context) |
| { |
| Map<Object, Object> 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<UIComponent> componentStack |
| = (List<UIComponent>) 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 = 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<UIComponent> componentStack |
| = (List<UIComponent>) 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 void pushComponentToEL(FacesContext context, UIComponent component) |
| { |
| if (component == null) |
| { |
| component = this; |
| } |
| |
| Map<Object, Object> 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<UIComponent> componentStack = (List<UIComponent>) |
| 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<UIComponent> componentStack = (List<UIComponent>) 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<String, UIComponent> 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<Object, Object> 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 = (param != null && Boolean.parseBoolean(param)); |
| attributes.put(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME, paramValue); |
| } |
| return paramValue; |
| } |
| |
| private static class BundleMap implements Map<String, String> |
| { |
| private ResourceBundle _bundle; |
| private List<String> _values; |
| |
| public BundleMap(ResourceBundle bundle) |
| { |
| _bundle = bundle; |
| } |
| |
| // Optimized methods |
| @Override |
| public String get(Object key) |
| { |
| try |
| { |
| return (String) _bundle.getObject(key.toString()); |
| } |
| catch (Exception e) |
| { |
| return "???" + key + "???"; |
| } |
| } |
| |
| @Override |
| public boolean isEmpty() |
| { |
| return !_bundle.getKeys().hasMoreElements(); |
| } |
| |
| @Override |
| public boolean containsKey(Object key) |
| { |
| try |
| { |
| return _bundle.getObject(key.toString()) != null; |
| } |
| catch (MissingResourceException e) |
| { |
| return false; |
| } |
| } |
| |
| // Unoptimized methods |
| @Override |
| public Collection<String> values() |
| { |
| if (_values == null) |
| { |
| _values = new ArrayList<>(); |
| for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); ) |
| { |
| String v = _bundle.getString(enumer.nextElement()); |
| _values.add(v); |
| } |
| } |
| return _values; |
| } |
| |
| @Override |
| public int size() |
| { |
| return values().size(); |
| } |
| |
| @Override |
| public boolean containsValue(Object value) |
| { |
| return values().contains(value); |
| } |
| |
| @Override |
| public Set<Map.Entry<String, String>> entrySet() |
| { |
| Set<Entry<String, String>> set = new HashSet<>(); |
| for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); ) |
| { |
| final String k = enumer.nextElement(); |
| set.add(new Map.Entry<String, String>() |
| { |
| @Override |
| public String getKey() |
| { |
| return k; |
| } |
| |
| @Override |
| public String getValue() |
| { |
| return (String) _bundle.getObject(k); |
| } |
| |
| @Override |
| public String setValue(String value) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| }); |
| } |
| |
| return set; |
| } |
| |
| @Override |
| public Set<String> keySet() |
| { |
| Set<String> set = new HashSet<>(); |
| for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); ) |
| { |
| set.add(enumer.nextElement()); |
| } |
| return set; |
| } |
| |
| @Override |
| public String remove(Object key) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void putAll(Map<? extends String, ? extends String> t) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String put(String key, String value) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| 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(); |
| } |
| |
| @Override |
| 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; |
| } |
| |
| @Override |
| 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); |
| } |
| |
| @Override |
| public void clearInitialState() |
| { |
| if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) |
| { |
| ((PartialStateHolder) listener).clearInitialState(); |
| } |
| _initialStateMarked = false; |
| } |
| |
| @Override |
| public boolean initialStateMarked() |
| { |
| if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) |
| { |
| return ((PartialStateHolder) listener).initialStateMarked(); |
| } |
| return _initialStateMarked; |
| } |
| |
| @Override |
| public void markInitialState() |
| { |
| if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0) |
| { |
| ((PartialStateHolder) listener).markInitialState(); |
| } |
| _initialStateMarked = true; |
| } |
| |
| @Override |
| public boolean isTransient() |
| { |
| if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 || |
| (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0) |
| { |
| return ((StateHolder) listener).isTransient(); |
| } |
| return false; |
| } |
| |
| @Override |
| 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) |
| { |
| 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]); |
| } |
| } |
| } |
| |
| @Override |
| public Object saveState(FacesContext context) |
| { |
| if (!initialStateMarked()) |
| { |
| 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; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void setTransient(boolean newTransientValue) |
| { |
| if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 || |
| (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0) |
| { |
| ((StateHolder) listener).setTransient(newTransientValue); |
| } |
| } |
| } |
| } |