/** * */ package org.richfaces.el; import java.beans.FeatureDescriptor; import java.util.Collection; import java.util.Iterator; import java.util.Locale; import java.util.Map; import javax.el.ELContext; import javax.el.ELResolver; import javax.el.FunctionMapper; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.faces.context.FacesContext; import javax.faces.el.CompositeComponentExpressionHolder; import org.richfaces.validator.GraphValidatorState; /** * This class wraps original ELContext and capture whole call stack to the target object so it could be used to extract semantic * information like annotations or Jena Model properties. * * @author asmirnov * */ public class CapturingELContext extends ELContext { private final ELContext parent; private ValueReference reference = null; private final InterceptingResolver resolver; public CapturingELContext(ELContext parent,Map<Object, GraphValidatorState> states) { this.parent = parent; resolver = new InterceptingResolver(parent.getELResolver(),states); } public ValueReference getReference() { return reference; } private boolean isContainerObject(Object base) { return base instanceof Collection || base instanceof Map || base.getClass().isArray(); } public boolean hasReferenceExpression() { return reference != null && reference.getBase() instanceof CompositeComponentExpressionHolder; } public ValueExpression getReferenceExpression() { CompositeComponentExpressionHolder expressionHolder = (CompositeComponentExpressionHolder) reference.getBase(); return expressionHolder.getExpression(reference.getProperty().toString()); } public ValueDescriptor getDescriptor() { ValueReference localReference = reference; while (true) { if (localReference == null || localReference.getBase() == null || localReference.getProperty() == null) { return null; } Object base = localReference.getBase(); if (isContainerObject(base) && localReference.hasNext()) { localReference = localReference.next(); } else { return new ValueDescriptor(base.getClass(), localReference.getProperty().toString()); } } } /* * (non-Javadoc) * * @see javax.el.ELContext#getELResolver() */ @Override public ELResolver getELResolver() { return resolver; } @SuppressWarnings("unchecked") @Override public Object getContext(Class key) { return parent.getContext(key); } @Override public FunctionMapper getFunctionMapper() { return parent.getFunctionMapper(); } @Override public Locale getLocale() { return parent.getLocale(); } @Override public VariableMapper getVariableMapper() { return parent.getVariableMapper(); } @SuppressWarnings("unchecked") @Override public void putContext(Class key, Object contextObject) { parent.putContext(key, contextObject); } @Override public void setLocale(Locale locale) { parent.setLocale(locale); } /** * This resolver records all intermediate objects from the EL-expression that can be used to detect Semantic Beans * annotations or Jena Model properties. * * @author asmirnov * */ private final class InterceptingResolver extends ELResolver { private final ELResolver delegate; private boolean clonedObject; private final Map<Object, GraphValidatorState> states; public InterceptingResolver(ELResolver delegate, Map<Object, GraphValidatorState> states) { this.delegate = delegate; this.states = states; } // Capture the base and property rather than write the value @Override public void setValue(ELContext context, Object base, Object property, Object value) { if (base != null) { // TODO - detect value object from inderect references ( e.g. data table variables ). if (this.clonedObject) { delegate.setValue(context, base, property, value); } context.setPropertyResolved(true); reference = new ValueReference(base, property, reference); } } // The rest of the methods simply delegate to the existing context @Override public Object getValue(ELContext context, Object base, Object property) { reference = new ValueReference(base, property, reference); Object value = delegate.getValue(context, base, property); if (null != value && context.isPropertyResolved() && states.containsKey(value)) { GraphValidatorState graphValidatorState = states.get(value); if (graphValidatorState.isActive()) { this.clonedObject = true; Object clone = graphValidatorState.getCloned(); return clone; } } return value; } @Override public Class<?> getType(ELContext context, Object base, Object property) { if (base != null) { context.setPropertyResolved(true); reference = new ValueReference(base, property, reference); } return delegate.getType(context, base, property); } @Override public boolean isReadOnly(ELContext context, Object base, Object property) { return delegate.isReadOnly(context, base, property); } @Override public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { return delegate.getFeatureDescriptors(context, base); } @Override public Class<?> getCommonPropertyType(ELContext context, Object base) { return delegate.getCommonPropertyType(context, base); } } }