blob: ef5896346a44f63c660501ea0b2d2c6f3b5c0ad8 [file] [log] [blame]
/*
* 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 org.apache.myfaces.extensions.validator.core.el;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.el.ELResolver;
import javax.el.ELContext;
import javax.el.VariableMapper;
import javax.el.FunctionMapper;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.beans.FeatureDescriptor;
/**
* @author Gerhard Petracek
* @since 1.x.1
*/
public class ExtValELResolver extends ELResolver
{
protected final Log logger = LogFactory.getLog(getClass());
private ELResolver wrapped;
private Object baseObject;
private String property;
//forms the id for cross-validation within complex components
private String expression;
private boolean isPathRecordingStopped = false;
public ExtValELResolver(ELResolver elResolver)
{
this.wrapped = elResolver;
}
public Object getBaseObject()
{
return baseObject;
}
public String getProperty()
{
return property;
}
public String getPath()
{
if(this.logger.isTraceEnabled())
{
this.logger.trace("extracted path: " + this.expression);
}
return this.expression;
}
public void reset()
{
this.baseObject = null;
this.property = null;
this.expression = null;
}
/**
* path recording is performed to get a key for cross-validation<br/>
* every base after the first call which is null stops recording<br/>
* with a dynamic map syntax the last property in the middle of an expression has to be a string.
* such a string result continues the path recording at the next call for the current expression.
* <p/>
* example: #{bean[bean.propertyNameProvider['nameOfProperty1']]['dynBean'].property}
* <p/>
* limitation for cross-validation: nameOfProperty1 has to be of type string.
* other key types aren't supported yet
*
* @param elContext wrapped el-context
* @param base current base
* @param property property to resolve
* @return result of the delegated method call
*/
public Object getValue(ELContext elContext, Object base, Object property)
{
Object result = this.wrapped.getValue(elContext, base, property);
//very first call for an expression
if(this.expression == null)
{
this.expression = (String)property;
}
//#{bean[dynBase.propertyName]} -> base of dynBase is null -> stop path recording
else if(base == null)
{
this.isPathRecordingStopped = true;
}
else
{
boolean propertyExists = false;
String propertyName = (String)property;
propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
try
{
if(base instanceof Map)
{
if(((Map)base).containsKey(property))
{
propertyExists = true;
}
}
else if(base.getClass().getMethod("get" + propertyName) != null)
{
propertyExists = true;
}
}
catch (NoSuchMethodException e)
{
try
{
if(base.getClass().getMethod("is" + propertyName) != null)
{
propertyExists = true;
}
}
catch (NoSuchMethodException e1)
{
if(this.logger.isTraceEnabled())
{
this.logger.trace("property: " + property +
" isn't used for path - it isn't a property of " + base.getClass());
}
}
}
//e.g.: #{bean.subBase.property} -> here we are at subBase
if(propertyExists && !this.isPathRecordingStopped)
{
this.expression += "." + property;
}
else if(propertyExists && result instanceof String)
{
this.isPathRecordingStopped = false;
}
}
/*
if(this.isPathRecordingStopped && result instanceof String)
{
this.expression += "." + property;
}
*/
return result;
}
public Class<?> getType(ELContext elContext, Object o, Object o1)
{
return wrapped.getType(elContext, o, o1);
}
public void setValue(ELContext elContext, Object o, Object o1, Object o2)
{
expression += "." + o1;
property = (String)o1;
baseObject = o;
elContext.setPropertyResolved(true);
}
public boolean isReadOnly(ELContext elContext, Object o, Object o1)
{
return wrapped.isReadOnly(elContext, o, o1);
}
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object o)
{
return wrapped.getFeatureDescriptors(elContext, o);
}
public Class<?> getCommonPropertyType(ELContext elContext, Object o)
{
return wrapped.getCommonPropertyType(elContext, o);
}
public static ELContext createContextWrapper(final ELContext context, final ELResolver resolver)
{
return new ELContext()
{
@Override
public Locale getLocale()
{
return context.getLocale();
}
@Override
public void setPropertyResolved(boolean value)
{
super.setPropertyResolved(value);
context.setPropertyResolved(value);
}
@Override
public void putContext(Class clazz, Object object)
{
super.putContext(clazz, object);
context.putContext(clazz, object);
}
@Override
public Object getContext(Class clazz)
{
return context.getContext(clazz);
}
@Override
public void setLocale(Locale locale)
{
super.setLocale(locale);
context.setLocale(locale);
}
@Override
public ELResolver getELResolver()
{
return resolver;
}
@Override
public FunctionMapper getFunctionMapper()
{
return context.getFunctionMapper();
}
@Override
public VariableMapper getVariableMapper()
{
return context.getVariableMapper();
}
};
}
}