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

com.sun.faces.el.CompositeComponentAttributesELResolver Maven / Gradle / Ivy

There is a newer version: 4.1.0-M1
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.faces.el;

import java.beans.FeatureDescriptor;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.HashMap;
import java.util.logging.Logger;

import javax.el.ELResolver;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.el.MethodExpression;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.CompositeComponentExpressionHolder;

import com.sun.faces.util.Util;
import com.sun.faces.component.CompositeComponentStackManager;
import static com.sun.faces.component.CompositeComponentStackManager.StackType.TreeCreation;
import static com.sun.faces.component.CompositeComponentStackManager.StackType.Evaluation;
import com.sun.faces.util.FacesLogger;

import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;

/**
 * 

* This {@link ELResolver} will handle the resolution of attrs * when processing a composite component instance. *

*/ public class CompositeComponentAttributesELResolver extends ELResolver { // Log instance for this class private static final Logger LOGGER = FacesLogger.CONTEXT.getLogger(); /** * Implicit object related only to the cc implicitObject. */ private static final String COMPOSITE_COMPONENT_ATTRIBUTES_NAME = "attrs"; /** * Implicit object related only to the cc implicit object * and refers to the composite component parent (if any). */ private static final String COMPOSITE_COMPONENT_PARENT_NAME = "parent"; /** * Key to which we store the mappings between composite component instances * and their ExpressionEvalMap. */ private static final String EVAL_MAP_KEY = CompositeComponentAttributesELResolver.class.getName() + "_EVAL_MAP"; // ------------------------------------------------- Methods from ELResolver /** *

* If base is a composite component and property * is attrs, return a new ExpressionEvalMap * which wraps the composite component's attributes map. *

* *

* The ExpressionEvalMap simple evaluates any {@link ValueExpression} * instances stored in the composite component's attribute map and returns * the result. *

* *

* If base is a composite component and property * is parent attempt to resolve the composite componet parent * of the current composite component by calling * {@link UIComponent#getCompositeComponentParent(javax.faces.component.UIComponent)}) * and returning that value. *

* * @see javax.el.ELResolver#getValue(javax.el.ELContext, Object, Object) * @see com.sun.faces.el.CompositeComponentAttributesELResolver.ExpressionEvalMap */ @Override public Object getValue(ELContext context, Object base, Object property) { Util.notNull("context", context); if (base != null && (base instanceof UIComponent) && UIComponent.isCompositeComponent((UIComponent) base) && property != null) { String propertyName = property.toString(); if (COMPOSITE_COMPONENT_ATTRIBUTES_NAME.equals(propertyName)) { UIComponent c = (UIComponent) base; context.setPropertyResolved(true); FacesContext ctx = (FacesContext) context.getContext(FacesContext.class); return getEvalMapFor(c, ctx); } if (COMPOSITE_COMPONENT_PARENT_NAME.equals(propertyName)) { UIComponent c = (UIComponent) base; context.setPropertyResolved(true); FacesContext ctx = (FacesContext) context.getContext(FacesContext.class); CompositeComponentStackManager m = CompositeComponentStackManager.getManager(ctx); UIComponent ccp = m.getParentCompositeComponent(TreeCreation, ctx, c); if (ccp == null) { ccp = m.getParentCompositeComponent(Evaluation, ctx, c); } return ccp; } } return null; } /** *

* Readonly, so return null. *

* * @see ELResolver#getType(javax.el.ELContext, Object, Object) */ @Override public Class getType(ELContext context, Object base, Object property) { Util.notNull("context", context); if (!(base instanceof ExpressionEvalMap && property instanceof String)) { return null; } Class exprType = null; Class metaType = null; ExpressionEvalMap evalMap = (ExpressionEvalMap) base; ValueExpression ve = evalMap.getExpression((String) property); if (ve != null) { exprType = ve.getType(context); } if (!"".equals(property)) { FacesContext facesContext = (FacesContext) context.getContext(FacesContext.class); UIComponent cc = UIComponent.getCurrentCompositeComponent(facesContext); BeanInfo metadata = (BeanInfo) cc.getAttributes().get(UIComponent.BEANINFO_KEY); assert(null != metadata); PropertyDescriptor[] attributes = metadata.getPropertyDescriptors(); if (null != attributes) { for (PropertyDescriptor cur : attributes) { if (property.equals(cur.getName())) { Object type = cur.getValue("type"); if (null != type) { assert(type instanceof Class); metaType = (Class) type; break; } } } } } if (metaType != null) { // override exprType only if metaType is narrower: if (exprType == null || exprType.isAssignableFrom(metaType)) { context.setPropertyResolved(true); return metaType; } } return exprType; } /** *

* This is a no-op. *

* * @see ELResolver#setValue(javax.el.ELContext, Object, Object, Object) */ @Override public void setValue(ELContext context, Object base, Object property, Object value) { Util.notNull("context", context); } /** *

* Readonly, so return true *

* * @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, Object, Object) */ @Override public boolean isReadOnly(ELContext context, Object base, Object property) { Util.notNull("context", context); return true; } /** *

* This ELResolver currently returns no feature descriptors * as we have no way to effectively iterate over the UIComponent * attributes Map. *

* * @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, Object) */ @Override public Iterator getFeatureDescriptors(ELContext context, Object base) { Util.notNull("context", context); return null; } /** *

* attrs is considered a String property. *

* * @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, Object) */ @Override public Class getCommonPropertyType(ELContext context, Object base) { Util.notNull("context", context); return String.class; } // --------------------------------------------------------- Private Methods /** *

* Creates (if necessary) and caches an ExpressionEvalMap * instance associated with the owning {@link UIComponent} *

* * @param c the owning {@link UIComponent} * @param ctx the {@link FacesContext} for the current request * @return an ExpressionEvalMap for the specified component */ public Map getEvalMapFor(UIComponent c, FacesContext ctx) { Map ctxAttributes = ctx.getAttributes(); //noinspection unchecked Map> topMap = (Map>) ctxAttributes.get(EVAL_MAP_KEY); Map evalMap = null; if (topMap == null) { topMap = new HashMap<>(); ctxAttributes.put(EVAL_MAP_KEY, topMap); evalMap = new ExpressionEvalMap(ctx, c); topMap.put(c, evalMap); } else { evalMap = topMap.get(c); if (evalMap == null) { evalMap = new ExpressionEvalMap(ctx, c); topMap.put(c, evalMap); } else { // JAVASERVERFACES-2508 - running as Portlet2 FacesContext must be updated for rendering, or // ExpressionEvalMap would have to be reconstructed for the second Portlet phase ((ExpressionEvalMap)evalMap).updateFacesContext(ctx); } } return evalMap; } // ---------------------------------------------------------- Nested Classes /** * Simple Map implementation to evaluate any ValueExpression * stored directly within the provided attributes map. */ private static final class ExpressionEvalMap implements Map, CompositeComponentExpressionHolder { private Map attributesMap; private PropertyDescriptor[] declaredAttributes; private Map declaredDefaultValues; private FacesContext ctx; private UIComponent cc; // -------------------------------------------------------- Constructors ExpressionEvalMap(FacesContext ctx, UIComponent cc) { this.cc = cc; this.attributesMap = cc.getAttributes(); BeanInfo metadata = (BeanInfo) this.attributesMap.get(UIComponent.BEANINFO_KEY); if (null != metadata) { this.declaredAttributes = metadata.getPropertyDescriptors(); this.declaredDefaultValues = new HashMap<>(5); } this.ctx = ctx; } // --------------------- Methods from CompositeComponentExpressionHolder @Override public ValueExpression getExpression(String name) { Object ve = cc.getValueExpression(name); return ((ve instanceof ValueExpression) ? (ValueExpression) ve : null); } // ---------------------------------------------------- Methods from Map @Override public int size() { throw new UnsupportedOperationException(); } @Override public boolean isEmpty() { throw new UnsupportedOperationException(); } @Override public boolean containsKey(Object key) { boolean result = attributesMap.containsKey(key); if (!result) { result = null != getDeclaredDefaultValue(key); } return result; } @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); } @Override public Object get(Object key) { Object v = attributesMap.get(key); if (v == null) { v = getDeclaredDefaultValue(key); if (v != null) { return ((ValueExpression) v).getValue(ctx.getELContext()); } } if (v != null && v instanceof MethodExpression) { return v; } return v; } @Override public Object put(String key, Object value) { // Unlinke AttributesMap.get() which will obtain a value from // a ValueExpression, AttributesMap.put(), when passed a value, // will never call ValueExpression.setValue(), so we have to take // matters into our own hands... ValueExpression ve = cc.getValueExpression(key); if (ve != null) { ve.setValue(ctx.getELContext(), value); } else { attributesMap.put(key, value); } return null; } @Override public Object remove(Object key) { throw new UnsupportedOperationException(); } @Override public void putAll(Map t) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public Set keySet() { throw new UnsupportedOperationException(); } @Override public Collection values() { throw new UnsupportedOperationException(); } @Override public Set> entrySet() { throw new UnsupportedOperationException(); } private Object getDeclaredDefaultValue(Object key) { Object result = null; // If it's not in the cache... if (!declaredDefaultValues.containsKey(key)) { // iterate through the property descriptors... boolean found = false; for (PropertyDescriptor cur : declaredAttributes) { // and if you find a match... if (cur.getName().equals(key)) { found = true; // put it in the cache, returning the value. declaredDefaultValues.put(key, result = cur.getValue("default")); break; } } // Otherwise, if no attribute was declared if (!found) { // put null into the cache for future lookups. declaredDefaultValues.put(key, null); } } else { // It's in the cache, just return the value. result = declaredDefaultValues.get(key); } return result; } public void updateFacesContext(FacesContext ctx) { if (this.ctx != ctx) { this.ctx = ctx; } } } }