
org.apache.beehive.controls.system.jdbc.parser.ReflectionFragment Maven / Gradle / Ivy
The newest version!
/*
* 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.
*
* $Header:$
*/
package org.apache.beehive.controls.system.jdbc.parser;
import org.apache.beehive.controls.api.ControlException;
import org.apache.beehive.controls.api.context.ControlBeanContext;
import org.apache.beehive.controls.system.jdbc.TypeMappingsFactory;
import org.apache.beehive.controls.system.jdbc.JdbcControl;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Represents a method parameter substitution into the SQL annotation's statement member. Delimited by '{' and '}'.
* Method parameter names must exactly match the name used in the SQL statement in order for the substitution to occur.
*
*
* SQL(statement="SELECT * FROM {tableName}")
* public void getAll(String tableName) throws SQLException;
*
*/
public final class ReflectionFragment extends SqlFragment {
private static final String PREPARED_STATEMENT_SUB_MARK = "?";
private static final Pattern s_parameterNamePattern = Pattern.compile("\\.");
private final String _parameterName;
private final String[] _nameQualifiers;
private int _sqlDataType;
/**
* Create a new ReflectionFragment with the specifed method parameter name.
*
* @param parameterName The name of the parameter whose value should be substituted at this location.
*/
protected ReflectionFragment(String parameterName) {
_parameterName = parameterName;
_sqlDataType = TypeMappingsFactory.TYPE_UNKNOWN;
_nameQualifiers = s_parameterNamePattern.split(_parameterName);
}
/**
* Create a new ReflectionFragment with the specified method parameter name and SQL type.
*
* @param parameterName The name of the parameter whose value should be substituted at this location.
* @param sqlDataType A String specifing the SQL data type for this parameter.
*/
protected ReflectionFragment(String parameterName, String sqlDataType) {
this(parameterName);
if (sqlDataType != null) {
_sqlDataType = TypeMappingsFactory.getInstance().convertStringToSQLType(sqlDataType);
}
}
/**
* Return text generated by this fragment for a PreparedStatement
*
* @param context A ControlBeanContext instance.
* @param m The annotated method.
* @param args The method's parameters
* @return Always returns a PREPARED_STATEMENT_SUB_MARK
*/
protected String getPreparedStatementText(ControlBeanContext context, Method m, Object[] args) {
// this reflection fragment may resolve to a JdbcControl.ComplexSqlFragment
// if so it changes the behavior a bit.
Object val = getParameterValue(context, m, args);
if (val instanceof JdbcControl.ComplexSqlFragment) {
return ((JdbcControl.ComplexSqlFragment)val).getSQL();
} else {
return PREPARED_STATEMENT_SUB_MARK;
}
}
/**
* A reflection fragment may evaluate to an JdbcControl.ComplexSqlFragment type,
* which requires additional steps to evaluate after reflection.
*
* @param context Control bean context.
* @param m Method.
* @param args Method args.
* @return true or false.
*/
protected boolean hasComplexValue(ControlBeanContext context, Method m, Object[] args) {
Object val = getParameterValue(context, m, args);
return val instanceof JdbcControl.ComplexSqlFragment;
}
/**
* Always true for ReflectionFragment.
*
* @return true
*/
protected boolean hasParamValue() { return true; }
/**
* Get the parameter name (as specified in the SQL statement).
*
* @return The parameter name.
*/
protected String getParameterName() { return _parameterName; }
/**
* Get a copy of the array of parameter name qualifiers.
*
* @return An array of parameter name qualifiers.
*/
protected String[] getParameterNameQualifiers() {
String[] nameQualifiersCopy = new String[_nameQualifiers.length];
System.arraycopy(_nameQualifiers, 0, nameQualifiersCopy, 0, _nameQualifiers.length);
return nameQualifiersCopy;
}
/**
* Get the SQL data type of this param.
*
* @return The SQL data type for this param.
*/
protected int getParamSqlDataType() { return _sqlDataType; }
/**
* For JUnit testing.
*
* @return The String value of this fragment.
*/
public String toString() { return PREPARED_STATEMENT_SUB_MARK; }
/**
* Get the value of this parameter.
*
* @param context ControlBeanContext instance to evaluate the parameter's value against.
* @param method Method instance to evaluate against.
* @param args Method argument values
* @return All parameter object values contained within this fragment
*/
protected Object[] getParameterValues(ControlBeanContext context, Method method, Object[] args) {
Object value = getParameterValue(context, method, args);
if (value instanceof JdbcControl.ComplexSqlFragment) {
JdbcControl.SQLParameter[] params = ((JdbcControl.ComplexSqlFragment)value).getParameters();
Object[] values = new Object[params.length];
for (int i = 0; i < params.length; i++) {
values[i] = params[i].value;
}
return values;
}
return new Object[]{value};
}
//
// /////////////////////////////////////////////// PRIVATE METHODS /////////////////////////////////////////////
//
/**
* Get the value from the method param.
* @param method
* @param args
* @return Value of reflected method param.
*/
private Object getParameterValue(ControlBeanContext context, Method method, Object[] args) {
Object value;
try {
value = context.getParameterValue(method, _nameQualifiers[0], args);
} catch (IllegalArgumentException iae) {
throw new ControlException("Invalid argument name in SQL statement: " + _nameQualifiers[0], iae);
}
for (int i = 1; i < _nameQualifiers.length; i++) {
// handle maps, properties, and fields...
value = extractValue(value, _nameQualifiers[i - 1], _nameQualifiers[i]);
}
return value;
}
/**
* Get the value from the referenced method parameter using java reflection
*
* @param aValue
* @param aName
* @param bName
* @return The value
*/
private Object extractValue(Object aValue, String aName, String bName) {
Class aClass = aValue.getClass();
Object value;
//
// a.isB() or a.getB()
//
String bNameCapped = Character.toUpperCase(bName.charAt(0)) + bName.substring(1);
Method getMethod = null;
Class retType;
//
// try a.isB() first, if found verify that a.isB() returns a boolean value,
// and that there is not also a a.getB() method - if there is except
//
try {
getMethod = aClass.getMethod("is" + bNameCapped, (Class[]) null);
retType = getMethod.getReturnType();
if (!(retType.equals(Boolean.class) ||
retType.equals(Boolean.TYPE))) {
// only boolean returns can be isB()
getMethod = null;
} else {
/**
* make sure ("get" + bNameCapped) does not exist as well
* see CR216159
*/
boolean getMethodFound = true;
try {
aClass.getMethod("get" + bNameCapped, (Class[])null);
} catch (NoSuchMethodException e) {
getMethodFound = false;
}
if (getMethodFound) {
throw new ControlException("Colliding field accsessors in user defined class '"
+ aClass.getName() + "' for field '" + bName
+ "'. Please use is for boolean fields and get name for other datatypes.");
}
}
} catch (NoSuchMethodException e) {
// ignore
}
//
// try a.getB() if a.isB() was not found.
//
if (getMethod == null) {
try {
getMethod = aClass.getMethod("get" + bNameCapped, (Class[])null);
} catch (NoSuchMethodException e) {
// ignore
}
}
if (getMethod != null) {
// OK- a.getB()
try {
value = getMethod.invoke(aValue, (Object[]) null);
} catch (IllegalAccessException e) {
throw new ControlException("Unable to access public method: " + e.toString());
} catch (java.lang.reflect.InvocationTargetException e) {
throw new ControlException("Exception thrown when executing : " + getMethod.getName() + "() to use as parameter");
}
return value;
}
//
// try a.b
//
try {
value = aClass.getField(bName).get(aValue);
return value;
} catch (NoSuchFieldException e) {
// ignore
} catch (IllegalAccessException e) {
// ignore
}
//
// try a.get(b)
//
if (aValue instanceof Map) {
try {
value = TypeMappingsFactory.getInstance().lookupType(aValue, new Object[]{bName});
return value;
} catch (Exception mapex) {
throw new ControlException("Exception thrown when executing Map.get() to resolve parameter" + mapex.toString());
}
}
// no other options...
throw new ControlException("Illegal argument in SQL statement: " + _parameterName
+ "; unable to find suitable method of retrieving property " + bName
+ " out of object " + aName + ".");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy