com.feilong.lib.ognl.ASTProperty Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feilong Show documentation
Show all versions of feilong Show documentation
feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.
// --------------------------------------------------------------------------
// Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// Neither the name of the Drew Davidson nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
// --------------------------------------------------------------------------
package com.feilong.lib.ognl;
import java.beans.IndexedPropertyDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Iterator;
import com.feilong.lib.ognl.enhance.ExpressionCompiler;
import com.feilong.lib.ognl.enhance.UnsupportedCompilationException;
/**
* @author Luke Blanshard ([email protected])
* @author Drew Davidson ([email protected])
*/
public class ASTProperty extends SimpleNode implements NodeType{
/**
*
*/
private static final long serialVersionUID = 4661736090951488512L;
private boolean _indexedAccess = false;
private Class _getterClass;
private Class _setterClass;
public ASTProperty(int id){
super(id);
}
public void setIndexedAccess(boolean value){
_indexedAccess = value;
}
/**
* Returns true if this property is itself an index reference.
*
* @return true if this property is an index reference, false otherwise.
*/
public boolean isIndexedAccess(){
return _indexedAccess;
}
/**
* Returns true if this property is described by an IndexedPropertyDescriptor and that if
* followed by an index specifier it will call the index get/set methods rather than go through
* property accessors.
*
* @param context
* the OgnlContext within which to perform the operation.
* @param source
* the Object (indexed property) from which to retrieve the indexed property type.
* @return the int representing the indexed property type of source.
* @throws OgnlException
* if source is not an indexed property.
*/
public int getIndexedPropertyType(OgnlContext context,Object source) throws OgnlException{
Class type = context.getCurrentType();
Class prevType = context.getPreviousType();
try{
if (!isIndexedAccess()){
Object property = getProperty(context, source);
if (property instanceof String){
return OgnlRuntime.getIndexedPropertyType(
context,
(source == null) ? null : OgnlRuntime.getCompiler().getInterfaceClass(source.getClass()),
(String) property);
}
}
return OgnlRuntime.INDEXED_PROPERTY_NONE;
}finally{
context.setCurrentObject(source);
context.setCurrentType(type);
context.setPreviousType(prevType);
}
}
public Object getProperty(OgnlContext context,Object source) throws OgnlException{
return _children[0].getValue(context, context.getRoot());
}
@Override
protected Object getValueBody(OgnlContext context,Object source) throws OgnlException{
Object property = getProperty(context, source);
Object result = OgnlRuntime.getProperty(context, source, property);
if (result == null){
result = OgnlRuntime.getNullHandler(OgnlRuntime.getTargetClass(source)).nullPropertyValue(context, source, property);
}
return result;
}
@Override
protected void setValueBody(OgnlContext context,Object target,Object value) throws OgnlException{
OgnlRuntime.setProperty(context, target, getProperty(context, target), value);
}
@Override
public boolean isNodeSimpleProperty(OgnlContext context) throws OgnlException{
return (_children != null) && (_children.length == 1) && ((SimpleNode) _children[0]).isConstant(context);
}
@Override
public Class getGetterClass(){
return _getterClass;
}
@Override
public Class getSetterClass(){
return _setterClass;
}
@Override
public String toString(){
String result;
if (isIndexedAccess()){
result = "[" + _children[0] + "]";
}else{
result = ((ASTConst) _children[0]).getValue().toString();
}
return result;
}
@Override
public String toGetSourceString(OgnlContext context,Object target){
if (context.getCurrentObject() == null){
throw new UnsupportedCompilationException("Current target is null.");
}
String result = "";
Method m = null;
try{
if (isIndexedAccess()){
Object value = _children[0].getValue(context, context.getRoot());
if (value == null || DynamicSubscript.class.isAssignableFrom(value.getClass())){
throw new UnsupportedCompilationException("Value passed as indexed property was null or not supported.");
}
// Get root cast string if the child is a type that needs it (like a nested ASTProperty)
String srcString = _children[0].toGetSourceString(context, context.getRoot());
srcString = ExpressionCompiler.getRootExpression(_children[0], context.getRoot(), context) + srcString;
if (ASTChain.class.isInstance(_children[0])){
String cast = (String) context.remove(ExpressionCompiler.PRE_CAST);
if (cast != null){
srcString = cast + srcString;
}
}
if (ASTConst.class.isInstance(_children[0]) && String.class.isInstance(context.getCurrentObject())){
srcString = "\"" + srcString + "\"";
}
// System.out.println("indexed getting with child srcString: " + srcString + " value class: " + value.getClass() + " and child: " + _children[0].getClass());
if (context.get("_indexedMethod") != null){
m = (Method) context.remove("_indexedMethod");
_getterClass = m.getReturnType();
Object indexedValue = OgnlRuntime.callMethod(context, target, m.getName(), new Object[] { value });
context.setCurrentType(_getterClass);
context.setCurrentObject(indexedValue);
context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass()));
return "." + m.getName() + "(" + srcString + ")";
}
PropertyAccessor p = OgnlRuntime.getPropertyAccessor(target.getClass());
Object currObj = context.getCurrentObject();
Class currType = context.getCurrentType();
Class prevType = context.getPreviousType();
Object indexVal = p.getProperty(context, target, value);
// reset current object for accessor
context.setCurrentObject(currObj);
context.setCurrentType(currType);
context.setPreviousType(prevType);
if (ASTConst.class.isInstance(_children[0]) && Number.class.isInstance(context.getCurrentObject())){
context.setCurrentType(OgnlRuntime.getPrimitiveWrapperClass(context.getCurrentObject().getClass()));
}
result = p.getSourceAccessor(context, target, srcString);
_getterClass = context.getCurrentType();
context.setCurrentObject(indexVal);
return result;
}
String name = ((ASTConst) _children[0]).getValue().toString();
if (!Iterator.class.isAssignableFrom(context.getCurrentObject().getClass())
|| (Iterator.class.isAssignableFrom(context.getCurrentObject().getClass()) && name.indexOf("next") < 0)){
Object currObj = target;
try{
target = getValue(context, context.getCurrentObject());
}catch (NoSuchPropertyException e){
try{
target = getValue(context, context.getRoot());
}catch (NoSuchPropertyException ex){
// ignore
}
}finally{
context.setCurrentObject(currObj);
}
}
PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(context.getCurrentObject().getClass(), name);
if (pd != null && pd.getReadMethod() != null
&& !context.getMemberAccess().isAccessible(context, context.getCurrentObject(), pd.getReadMethod(), name)){
throw new UnsupportedCompilationException(
"Member access forbidden for property " + name + " on class " + context.getCurrentObject().getClass());
}
if (this.getIndexedPropertyType(context, context.getCurrentObject()) > 0 && pd != null){
// if an indexed method accessor need to use special property descriptors to find methods
if (pd instanceof IndexedPropertyDescriptor){
m = ((IndexedPropertyDescriptor) pd).getIndexedReadMethod();
}else{
if (pd instanceof ObjectIndexedPropertyDescriptor){
m = ((ObjectIndexedPropertyDescriptor) pd).getIndexedReadMethod();
}else{
throw new OgnlException("property '" + name + "' is not an indexed property");
}
}
if (_parent == null){
// the above pd will be the wrong result sometimes, such as methods like getValue(int) vs String[] getValue()
m = OgnlRuntime.getReadMethod(context.getCurrentObject().getClass(), name);
result = m.getName() + "()";
_getterClass = m.getReturnType();
}else{
context.put("_indexedMethod", m);
}
}else{
PropertyAccessor pa = OgnlRuntime.getPropertyAccessor(context.getCurrentObject().getClass());
if (context.getCurrentObject().getClass().isArray()){
if (pd == null){
pd = OgnlRuntime.getProperty(context.getCurrentObject().getClass(), name);
if (pd != null && pd.getReadMethod() != null){
m = pd.getReadMethod();
result = pd.getName();
}else{
_getterClass = int.class;
context.setCurrentAccessor(context.getCurrentObject().getClass());
context.setCurrentType(int.class);
result = "." + name;
}
}
}else{
if (pd != null && pd.getReadMethod() != null){
m = pd.getReadMethod();
result = "." + m.getName() + "()";
}else if (pa != null){
Object currObj = context.getCurrentObject();
Class currType = context.getCurrentType();
Class prevType = context.getPreviousType();
String srcString = _children[0].toGetSourceString(context, context.getRoot());
if (ASTConst.class.isInstance(_children[0]) && String.class.isInstance(context.getCurrentObject())){
srcString = "\"" + srcString + "\"";
}
context.setCurrentObject(currObj);
context.setCurrentType(currType);
context.setPreviousType(prevType);
result = pa.getSourceAccessor(context, context.getCurrentObject(), srcString);
_getterClass = context.getCurrentType();
}
}
}
}catch (Throwable t){
throw OgnlOps.castToRuntime(t);
}
// set known property types for NodeType interface when possible
if (m != null){
_getterClass = m.getReturnType();
context.setCurrentType(m.getReturnType());
context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass()));
}
context.setCurrentObject(target);
return result;
}
Method getIndexedWriteMethod(PropertyDescriptor pd){
if (IndexedPropertyDescriptor.class.isInstance(pd)){
return ((IndexedPropertyDescriptor) pd).getIndexedWriteMethod();
}else if (ObjectIndexedPropertyDescriptor.class.isInstance(pd)){
return ((ObjectIndexedPropertyDescriptor) pd).getIndexedWriteMethod();
}
return null;
}
@Override
public String toSetSourceString(OgnlContext context,Object target){
String result = "";
Method m = null;
if (context.getCurrentObject() == null){
throw new UnsupportedCompilationException("Current target is null.");
}
try{
if (isIndexedAccess()){
Object value = _children[0].getValue(context, context.getRoot());
if (value == null){
throw new UnsupportedCompilationException(
"Value passed as indexed property is null, can't enhance statement to bytecode.");
}
String srcString = _children[0].toGetSourceString(context, context.getRoot());
srcString = ExpressionCompiler.getRootExpression(_children[0], context.getRoot(), context) + srcString;
if (ASTChain.class.isInstance(_children[0])){
String cast = (String) context.remove(ExpressionCompiler.PRE_CAST);
if (cast != null){
srcString = cast + srcString;
}
}
if (ASTConst.class.isInstance(_children[0]) && String.class.isInstance(context.getCurrentObject())){
srcString = "\"" + srcString + "\"";
}
if (context.get("_indexedMethod") != null){
m = (Method) context.remove("_indexedMethod");
PropertyDescriptor pd = (PropertyDescriptor) context.remove("_indexedDescriptor");
boolean lastChild = lastChild(context);
if (lastChild){
m = getIndexedWriteMethod(pd);
if (m == null){
throw new UnsupportedCompilationException("Indexed property has no corresponding write method.");
}
}
_setterClass = m.getParameterTypes()[0];
Object indexedValue = null;
if (!lastChild){
indexedValue = OgnlRuntime.callMethod(context, target, m.getName(), new Object[] { value });
}
context.setCurrentType(_setterClass);
context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass()));
if (!lastChild){
context.setCurrentObject(indexedValue);
return "." + m.getName() + "(" + srcString + ")";
}
return "." + m.getName() + "(" + srcString + ", $3)";
}
PropertyAccessor p = OgnlRuntime.getPropertyAccessor(target.getClass());
Object currObj = context.getCurrentObject();
Class currType = context.getCurrentType();
Class prevType = context.getPreviousType();
Object indexVal = p.getProperty(context, target, value);
// reset current object for accessor
context.setCurrentObject(currObj);
context.setCurrentType(currType);
context.setPreviousType(prevType);
if (ASTConst.class.isInstance(_children[0]) && Number.class.isInstance(context.getCurrentObject())){
context.setCurrentType(OgnlRuntime.getPrimitiveWrapperClass(context.getCurrentObject().getClass()));
}
result = lastChild(context) ? p.getSourceSetter(context, target, srcString)
: p.getSourceAccessor(context, target, srcString);
//result = p.getSourceAccessor(context, target, srcString);
_getterClass = context.getCurrentType();
context.setCurrentObject(indexVal);
return result;
}
String name = ((ASTConst) _children[0]).getValue().toString();
if (!Iterator.class.isAssignableFrom(context.getCurrentObject().getClass())
|| (Iterator.class.isAssignableFrom(context.getCurrentObject().getClass()) && name.indexOf("next") < 0)){
Object currObj = target;
try{
target = getValue(context, context.getCurrentObject());
}catch (NoSuchPropertyException e){
try{
target = getValue(context, context.getRoot());
}catch (NoSuchPropertyException ex){}
}finally{
context.setCurrentObject(currObj);
}
}
PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(
OgnlRuntime.getCompiler().getInterfaceClass(context.getCurrentObject().getClass()),
name);
if (pd != null){
Method pdMethod = lastChild(context) ? pd.getWriteMethod() : pd.getReadMethod();
if (pdMethod != null && !context.getMemberAccess().isAccessible(context, context.getCurrentObject(), pdMethod, name)){
throw new UnsupportedCompilationException(
"Member access forbidden for property " + name + " on class " + context.getCurrentObject().getClass());
}
}
if (pd != null && this.getIndexedPropertyType(context, context.getCurrentObject()) > 0){
// if an indexed method accessor need to use special property descriptors to find methods
if (pd instanceof IndexedPropertyDescriptor){
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
m = lastChild(context) ? ipd.getIndexedWriteMethod() : ipd.getIndexedReadMethod();
}else{
if (pd instanceof ObjectIndexedPropertyDescriptor){
ObjectIndexedPropertyDescriptor opd = (ObjectIndexedPropertyDescriptor) pd;
m = lastChild(context) ? opd.getIndexedWriteMethod() : opd.getIndexedReadMethod();
}else{
throw new OgnlException("property '" + name + "' is not an indexed property");
}
}
if (_parent == null){
// the above pd will be the wrong result sometimes, such as methods like getValue(int) vs String[] getValue()
m = OgnlRuntime.getWriteMethod(context.getCurrentObject().getClass(), name);
Class parm = m.getParameterTypes()[0];
String cast = parm.isArray() ? ExpressionCompiler.getCastString(parm) : parm.getName();
result = m.getName() + "((" + cast + ")$3)";
_setterClass = parm;
}else{
context.put("_indexedMethod", m);
context.put("_indexedDescriptor", pd);
}
}else{
PropertyAccessor pa = OgnlRuntime.getPropertyAccessor(context.getCurrentObject().getClass());
if (target != null){
_setterClass = target.getClass();
}
if (_parent != null && pd != null && pa == null){
m = pd.getReadMethod();
result = m.getName() + "()";
}else{
if (context.getCurrentObject().getClass().isArray()){
result = "";
}else if (pa != null){
Object currObj = context.getCurrentObject();
String srcString = _children[0].toGetSourceString(context, context.getRoot());
if (ASTConst.class.isInstance(_children[0]) && String.class.isInstance(context.getCurrentObject())){
srcString = "\"" + srcString + "\"";
}
context.setCurrentObject(currObj);
if (!lastChild(context)){
result = pa.getSourceAccessor(context, context.getCurrentObject(), srcString);
}else{
result = pa.getSourceSetter(context, context.getCurrentObject(), srcString);
}
_getterClass = context.getCurrentType();
}
}
}
}catch (Throwable t){
throw OgnlOps.castToRuntime(t);
}
context.setCurrentObject(target);
if (m != null){
context.setCurrentType(m.getReturnType());
context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass()));
}
return result;
}
}