diff options
author | James Moger <james.moger@gitblit.com> | 2016-04-05 12:27:55 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2016-04-05 12:27:55 -0400 |
commit | bb87e621eefd97872aa1a619d38c166b2f07db84 (patch) | |
tree | d7199be13f0015e218ae2ded4ebd8af11b955845 /src/main/java/com/iciql/DaoProxy.java | |
parent | 98bb5dc01796728de5b18f84e19766276d12d1db (diff) | |
download | iciql-bb87e621eefd97872aa1a619d38c166b2f07db84.tar.gz iciql-bb87e621eefd97872aa1a619d38c166b2f07db84.zip |
Reformat project with default IntelliJ settings
Diffstat (limited to 'src/main/java/com/iciql/DaoProxy.java')
-rw-r--r-- | src/main/java/com/iciql/DaoProxy.java | 1251 |
1 files changed, 625 insertions, 626 deletions
diff --git a/src/main/java/com/iciql/DaoProxy.java b/src/main/java/com/iciql/DaoProxy.java index 34187c4..8851a0c 100644 --- a/src/main/java/com/iciql/DaoProxy.java +++ b/src/main/java/com/iciql/DaoProxy.java @@ -15,6 +15,11 @@ */ package com.iciql; +import com.iciql.Iciql.DataTypeAdapter; +import com.iciql.util.JdbcUtils; +import com.iciql.util.StringUtils; +import com.iciql.util.Utils; + import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -37,503 +42,497 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.iciql.Iciql.DataTypeAdapter; -import com.iciql.util.JdbcUtils; -import com.iciql.util.StringUtils; -import com.iciql.util.Utils; - /** * DaoProxy creates a dynamic instance of the provided Dao interface. * - * @author James Moger - * * @param <X> + * @author James Moger */ final class DaoProxy<X extends Dao> implements InvocationHandler, Dao { - private final Db db; + private final Db db; - private final Class<X> daoInterface; + private final Class<X> daoInterface; - private final char bindingDelimiter = ':'; + private final char bindingDelimiter = ':'; - private final Map<Method, IndexedSql> indexedSqlCache; + private final Map<Method, IndexedSql> indexedSqlCache; - DaoProxy(Db db, Class<X> daoInterface) { - this.db = db; - this.daoInterface = daoInterface; - this.indexedSqlCache = new ConcurrentHashMap<Method, IndexedSql>(); - } + DaoProxy(Db db, Class<X> daoInterface) { + this.db = db; + this.daoInterface = daoInterface; + this.indexedSqlCache = new ConcurrentHashMap<Method, IndexedSql>(); + } - /** - * Builds a proxy object for the DAO interface. - * - * @return a proxy object - */ - @SuppressWarnings("unchecked") - X build() { + /** + * Builds a proxy object for the DAO interface. + * + * @return a proxy object + */ + @SuppressWarnings("unchecked") + X build() { - if (!daoInterface.isInterface()) { - throw new IciqlException("Dao {0} must be an interface!", daoInterface.getName()); - } + if (!daoInterface.isInterface()) { + throw new IciqlException("Dao {0} must be an interface!", daoInterface.getName()); + } - ClassLoader classLoader = daoInterface.getClassLoader(); + ClassLoader classLoader = daoInterface.getClassLoader(); - Set<Class<?>> interfaces = new HashSet<Class<?>>(); - interfaces.add(Dao.class); - interfaces.add(daoInterface); - for (Class<?> clazz : daoInterface.getInterfaces()) { - interfaces.add(clazz); - } + Set<Class<?>> interfaces = new HashSet<Class<?>>(); + interfaces.add(Dao.class); + interfaces.add(daoInterface); + for (Class<?> clazz : daoInterface.getInterfaces()) { + interfaces.add(clazz); + } - Class<?>[] constructorParams = { InvocationHandler.class }; - Class<?>[] allInterfaces = interfaces.toArray(new Class<?>[interfaces.size()]); + Class<?>[] constructorParams = {InvocationHandler.class}; + Class<?>[] allInterfaces = interfaces.toArray(new Class<?>[interfaces.size()]); - try { + try { - Class<?> proxyClass = Proxy.getProxyClass(classLoader, allInterfaces); - Constructor<?> proxyConstructor = proxyClass.getConstructor(constructorParams); - return (X) proxyConstructor.newInstance(new Object[] { this }); + Class<?> proxyClass = Proxy.getProxyClass(classLoader, allInterfaces); + Constructor<?> proxyConstructor = proxyClass.getConstructor(constructorParams); + return (X) proxyConstructor.newInstance(new Object[]{this}); - } catch (Exception e) { - throw new IciqlException(e); - } - } + } catch (Exception e) { + throw new IciqlException(e); + } + } - /** - * Invoke intercepts method calls and delegates execution to the appropriate object. - */ - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - try { + /** + * Invoke intercepts method calls and delegates execution to the appropriate object. + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { - if (method.getDeclaringClass() == Dao.class) { + if (method.getDeclaringClass() == Dao.class) { - return method.invoke(this, args); + return method.invoke(this, args); - } else if (method.isAnnotationPresent(SqlQuery.class)) { + } else if (method.isAnnotationPresent(SqlQuery.class)) { - String sql = method.getAnnotation(SqlQuery.class).value(); - String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); - return executeQuery(method, args, statement); + String sql = method.getAnnotation(SqlQuery.class).value(); + String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); + return executeQuery(method, args, statement); - } else if (method.isAnnotationPresent(SqlStatement.class)) { + } else if (method.isAnnotationPresent(SqlStatement.class)) { - String sql = method.getAnnotation(SqlStatement.class).value(); - String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); - return executeStatement(method, args, statement); + String sql = method.getAnnotation(SqlStatement.class).value(); + String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); + return executeStatement(method, args, statement); - } else { + } else { - throw new IciqlException("Can not invoke non-dao method {0}.{1}", - method.getDeclaringClass().getSimpleName(), method.getName()); + throw new IciqlException("Can not invoke non-dao method {0}.{1}", + method.getDeclaringClass().getSimpleName(), method.getName()); - } + } - } catch (InvocationTargetException te) { - throw te.getCause(); - } - } + } catch (InvocationTargetException te) { + throw te.getCause(); + } + } - /** - * Execute a query. - * - * @param method - * @param methodArgs - * @param sql - * @return the result - */ - private Object executeQuery(Method method, Object[] methodArgs, String sql) { + /** + * Execute a query. + * + * @param method + * @param methodArgs + * @param sql + * @return the result + */ + private Object executeQuery(Method method, Object[] methodArgs, String sql) { /* - * Determine and validate the return type + * Determine and validate the return type */ - Class<?> returnType = method.getReturnType(); - - if (void.class == returnType) { - throw new IciqlException("You must specify a return type for @{0} {1}.{2}!", - SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); - } - - if (Collection.class.isAssignableFrom(returnType)) { - throw new IciqlException("You may not return a collection for an @{0} method, please change the return type of {1}.{2} to YourClass[]!", - SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); - } - - boolean isArray = false; - if (returnType.isArray()) { - isArray = true; - returnType = returnType.getComponentType(); - } - - boolean isJavaType = returnType.isEnum() - || returnType.isPrimitive() - || java.lang.Boolean.class.isAssignableFrom(returnType) - || java.lang.Number.class.isAssignableFrom(returnType) - || java.lang.String.class.isAssignableFrom(returnType) - || java.util.Date.class.isAssignableFrom(returnType) - || byte[].class.isAssignableFrom(returnType); - - Class<? extends DataTypeAdapter<?>> adapter = Utils.getDataTypeAdapter(method.getAnnotations()); - if (adapter == null) { - adapter = Utils.getDataTypeAdapter(returnType.getAnnotations()); - } + Class<?> returnType = method.getReturnType(); + + if (void.class == returnType) { + throw new IciqlException("You must specify a return type for @{0} {1}.{2}!", + SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); + } + + if (Collection.class.isAssignableFrom(returnType)) { + throw new IciqlException("You may not return a collection for an @{0} method, please change the return type of {1}.{2} to YourClass[]!", + SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); + } + + boolean isArray = false; + if (returnType.isArray()) { + isArray = true; + returnType = returnType.getComponentType(); + } + + boolean isJavaType = returnType.isEnum() + || returnType.isPrimitive() + || java.lang.Boolean.class.isAssignableFrom(returnType) + || java.lang.Number.class.isAssignableFrom(returnType) + || java.lang.String.class.isAssignableFrom(returnType) + || java.util.Date.class.isAssignableFrom(returnType) + || byte[].class.isAssignableFrom(returnType); + + Class<? extends DataTypeAdapter<?>> adapter = Utils.getDataTypeAdapter(method.getAnnotations()); + if (adapter == null) { + adapter = Utils.getDataTypeAdapter(returnType.getAnnotations()); + } /* * Prepare & execute sql */ - PreparedSql preparedSql = prepareSql(method, methodArgs, sql); + PreparedSql preparedSql = prepareSql(method, methodArgs, sql); - List<Object> objects; - if (!isJavaType && adapter == null) { + List<Object> objects; + if (!isJavaType && adapter == null) { - // query of an Iciql model - objects = db.executeQuery(returnType, preparedSql.sql, preparedSql.parameters); + // query of an Iciql model + objects = db.executeQuery(returnType, preparedSql.sql, preparedSql.parameters); - } else { + } else { - // query of (array of) standard Java type or a DataTypeAdapter type - objects = Utils.newArrayList(); - ResultSet rs = db.executeQuery(preparedSql.sql, preparedSql.parameters); - try { + // query of (array of) standard Java type or a DataTypeAdapter type + objects = Utils.newArrayList(); + ResultSet rs = db.executeQuery(preparedSql.sql, preparedSql.parameters); + try { - while (rs.next()) { + while (rs.next()) { - Object value = db.getDialect().deserialize(rs, 1, returnType, adapter); - objects.add(value); + Object value = db.getDialect().deserialize(rs, 1, returnType, adapter); + objects.add(value); - if (!isArray) { - // we are not returning an array so we break - // the loop and return the first result - break; - } - } + if (!isArray) { + // we are not returning an array so we break + // the loop and return the first result + break; + } + } - } catch (SQLException e) { - throw new IciqlException(e); - } finally { - JdbcUtils.closeSilently(rs); - } + } catch (SQLException e) { + throw new IciqlException(e); + } finally { + JdbcUtils.closeSilently(rs); + } - } + } /* * Return the results */ - if (objects == null || objects.isEmpty()) { + if (objects == null || objects.isEmpty()) { - // no results - if (isArray) { - // return an empty array - return Array.newInstance(returnType, 0); - } + // no results + if (isArray) { + // return an empty array + return Array.newInstance(returnType, 0); + } - // nothing to return! - return null; + // nothing to return! + return null; - } else if (isArray) { + } else if (isArray) { - // return an array of object results - Object array = Array.newInstance(returnType, objects.size()); - for (int i = 0; i < objects.size(); i++) { - Array.set(array, i, objects.get(i)); - } - return array; + // return an array of object results + Object array = Array.newInstance(returnType, objects.size()); + for (int i = 0; i < objects.size(); i++) { + Array.set(array, i, objects.get(i)); + } + return array; - } + } - // return first element - return objects.get(0); - } + // return first element + return objects.get(0); + } - /** - * Execute a statement. - * - * @param method - * @param methodArgs - * @param sql - * @return the result - */ - private Object executeStatement(Method method, Object[] methodArgs, String sql) { + /** + * Execute a statement. + * + * @param method + * @param methodArgs + * @param sql + * @return the result + */ + private Object executeStatement(Method method, Object[] methodArgs, String sql) { /* * Determine and validate the return type */ - Class<?> returnType = method.getReturnType(); + Class<?> returnType = method.getReturnType(); - if (void.class != returnType && boolean.class != returnType && int.class != returnType) { + if (void.class != returnType && boolean.class != returnType && int.class != returnType) { - throw new IciqlException("Invalid return type '{0}' for @{1} {2}.{3}!", - returnType.getSimpleName(), SqlQuery.class.getSimpleName(), - method.getDeclaringClass().getSimpleName(), method.getName()); - } + throw new IciqlException("Invalid return type '{0}' for @{1} {2}.{3}!", + returnType.getSimpleName(), SqlQuery.class.getSimpleName(), + method.getDeclaringClass().getSimpleName(), method.getName()); + } /* * Prepare & execute sql */ - PreparedSql preparedSql = prepareSql(method, methodArgs, sql); - int rows = db.executeUpdate(preparedSql.sql, preparedSql.parameters); + PreparedSql preparedSql = prepareSql(method, methodArgs, sql); + int rows = db.executeUpdate(preparedSql.sql, preparedSql.parameters); /* * Return the results */ - if (void.class == returnType) { - - // return nothing - return null; - - } else if (boolean.class == returnType) { - - // return true if any rows were affected - return rows > 0; - - } else { - - // return number of rows - return rows; - - } - } - - /** - * Prepares an sql statement and execution parameters based on the supplied - * method and it's arguments. - * - * @param method - * @param methodArgs - * @param sql - * @return a prepared sql statement and arguments - */ - private PreparedSql prepareSql(Method method, Object[] methodArgs, String sql) { - - if (methodArgs == null || methodArgs.length == 0) { - // no method arguments - return new PreparedSql(sql, null); - } - - IndexedSql indexedSql = indexedSqlCache.get(method); - - if (indexedSql == null) { - - // index the sql and method args - indexedSql = indexSql(method, sql); - - // cache the indexed sql for re-use - indexedSqlCache.put(method, indexedSql); - } - - final PreparedSql preparedSql = indexedSql.prepareSql(db, methodArgs); - return preparedSql; - } - - /** - * Indexes an sql statement and method args based on the supplied - * method and it's arguments. - * - * @param method - * @param sql - * @return an indexed sql statement and arguments - */ - private IndexedSql indexSql(Method method, String sql) { - - Map<String, IndexedArgument> parameterIndex = buildParameterIndex(method); - - // build a regex to extract parameter names from the sql statement - StringBuilder sb = new StringBuilder(); - sb.append(bindingDelimiter); - sb.append("{1}(\\?"); - for (String name : parameterIndex.keySet()) { - sb.append("|"); - // strip binding delimeter from name - sb.append(name); - } - sb.append(')'); - - // identify parameters, replace with the '?' PreparedStatement - // delimiter and build the PreparedStatement parameters array - final String regex = sb.toString(); - final Pattern p = Pattern.compile(regex); - final Matcher m = p.matcher(sql); - final StringBuffer buffer = new StringBuffer(); - - List<IndexedArgument> indexedArgs = Utils.newArrayList(); - int count = 0; - while (m.find()) { - String binding = m.group(1); - m.appendReplacement(buffer, "?"); - - IndexedArgument indexedArg; - if ("?".equals(binding)) { - // standard ? JDBC placeholder - indexedArg = parameterIndex.get("arg" + count); - } else { - // named placeholder - indexedArg = parameterIndex.get(binding); - } - - if (indexedArg == null) { - throw new IciqlException("Unbound SQL parameter '{0}' in {1}.{2}", - binding, method.getDeclaringClass().getSimpleName(), method.getName()); - } - indexedArgs.add(indexedArg); - - count++; - } - m.appendTail(buffer); - - final String statement = buffer.toString(); - - // create an IndexedSql container for the statement and indexes - return new IndexedSql(statement, Collections.unmodifiableList(indexedArgs)); - - } - - /** - * Builds an index of parameter name->(position,typeAdapter) from the method arguments - * array. This index is calculated once per method. - * - * @param method - * @return a bindings map of ("name", IndexedArgument) pairs - */ - private Map<String, IndexedArgument> buildParameterIndex(Method method) { - - Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>(); - - Annotation [][] annotationsMatrix = method.getParameterAnnotations(); - for (int i = 0; i < annotationsMatrix.length; i++) { - - Annotation [] annotations = annotationsMatrix[i]; + if (void.class == returnType) { + + // return nothing + return null; + + } else if (boolean.class == returnType) { + + // return true if any rows were affected + return rows > 0; + + } else { + + // return number of rows + return rows; + + } + } + + /** + * Prepares an sql statement and execution parameters based on the supplied + * method and it's arguments. + * + * @param method + * @param methodArgs + * @param sql + * @return a prepared sql statement and arguments + */ + private PreparedSql prepareSql(Method method, Object[] methodArgs, String sql) { + + if (methodArgs == null || methodArgs.length == 0) { + // no method arguments + return new PreparedSql(sql, null); + } + + IndexedSql indexedSql = indexedSqlCache.get(method); + + if (indexedSql == null) { + + // index the sql and method args + indexedSql = indexSql(method, sql); + + // cache the indexed sql for re-use + indexedSqlCache.put(method, indexedSql); + } + + final PreparedSql preparedSql = indexedSql.prepareSql(db, methodArgs); + return preparedSql; + } + + /** + * Indexes an sql statement and method args based on the supplied + * method and it's arguments. + * + * @param method + * @param sql + * @return an indexed sql statement and arguments + */ + private IndexedSql indexSql(Method method, String sql) { + + Map<String, IndexedArgument> parameterIndex = buildParameterIndex(method); + + // build a regex to extract parameter names from the sql statement + StringBuilder sb = new StringBuilder(); + sb.append(bindingDelimiter); + sb.append("{1}(\\?"); + for (String name : parameterIndex.keySet()) { + sb.append("|"); + // strip binding delimeter from name + sb.append(name); + } + sb.append(')'); + + // identify parameters, replace with the '?' PreparedStatement + // delimiter and build the PreparedStatement parameters array + final String regex = sb.toString(); + final Pattern p = Pattern.compile(regex); + final Matcher m = p.matcher(sql); + final StringBuffer buffer = new StringBuffer(); + + List<IndexedArgument> indexedArgs = Utils.newArrayList(); + int count = 0; + while (m.find()) { + String binding = m.group(1); + m.appendReplacement(buffer, "?"); + + IndexedArgument indexedArg; + if ("?".equals(binding)) { + // standard ? JDBC placeholder + indexedArg = parameterIndex.get("arg" + count); + } else { + // named placeholder + indexedArg = parameterIndex.get(binding); + } + + if (indexedArg == null) { + throw new IciqlException("Unbound SQL parameter '{0}' in {1}.{2}", + binding, method.getDeclaringClass().getSimpleName(), method.getName()); + } + indexedArgs.add(indexedArg); + + count++; + } + m.appendTail(buffer); + + final String statement = buffer.toString(); + + // create an IndexedSql container for the statement and indexes + return new IndexedSql(statement, Collections.unmodifiableList(indexedArgs)); + + } + + /** + * Builds an index of parameter name->(position,typeAdapter) from the method arguments + * array. This index is calculated once per method. + * + * @param method + * @return a bindings map of ("name", IndexedArgument) pairs + */ + private Map<String, IndexedArgument> buildParameterIndex(Method method) { + + Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>(); + + Annotation[][] annotationsMatrix = method.getParameterAnnotations(); + for (int i = 0; i < annotationsMatrix.length; i++) { + + Annotation[] annotations = annotationsMatrix[i]; /* * Conditionally map the bean properties of the method argument * class to Method and Field instances. */ - BindBean bean = getAnnotation(BindBean.class, annotations); - if (bean != null) { - final String prefix = bean.value(); - final Class<?> argumentClass = method.getParameterTypes()[i]; - Map<String, IndexedArgument> beanIndex = buildBeanIndex(i, prefix, argumentClass); - index.putAll(beanIndex); - } - - Class<? extends DataTypeAdapter<?>> typeAdapter = Utils.getDataTypeAdapter(annotations); - final IndexedArgument indexedArgument = new IndexedArgument(i, typeAdapter); - - // :N - 1-indexed, like JDBC ResultSet - index.put("" + (i + 1), indexedArgument); - - // argN - 0-indexed, like Reflection - index.put("arg" + i, indexedArgument); - - // Bound name - Bind binding = getAnnotation(Bind.class, annotations); - if (binding!= null && !binding.value().isEmpty()) { - index.put(binding.value(), indexedArgument); - } - - // try mapping Java 8 argument names, may overwrite argN - try { - Class<?> nullArgs = null; - Method getParameters = method.getClass().getMethod("getParameters", nullArgs); - if (getParameters != null) { - Object [] parameters = (Object []) getParameters.invoke(method, nullArgs); - if (parameters != null) { - Object o = parameters[i]; - Method getName = o.getClass().getMethod("getName", nullArgs); - String j8name = getName.invoke(o, nullArgs).toString(); - if (!j8name.isEmpty()) { - index.put(j8name, indexedArgument); - } - } - } - } catch (Throwable t) { - } - } - - return index; - } - - /** - * Builds an index of parameter name->(position,method) from the method arguments - * array. This index is calculated once per method. - * - * @param argumentIndex - * @param prefix - * @param beanClass - * @return a bindings map of ("prefix.property", IndexedArgument) pairs - */ - private Map<String, IndexedArgument> buildBeanIndex(int argumentIndex, String prefix, Class<?> beanClass) { - - final String beanPrefix = StringUtils.isNullOrEmpty(prefix) ? "" : (prefix + "."); - final Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>(); - - // map JavaBean property getters - for (Method method : beanClass.getMethods()) { - - if (Modifier.isStatic(method.getModifiers()) - || method.getReturnType() == void.class - || method.getParameterTypes().length > 0 - || method.getDeclaringClass() == Object.class) { - - // not a JavaBean property - continue; - } - - final String propertyName; - final String name = method.getName(); - if (name.startsWith("get")) { - propertyName = method.getName().substring(3); - } else if (name.startsWith("is")) { - propertyName = method.getName().substring(2); - } else { - propertyName = null; - } - - if (propertyName == null) { - // not a conventional JavaBean property - continue; - } - - final String binding = beanPrefix + preparePropertyName(propertyName); - final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, method); - - index.put(binding, indexedArg); - } - - // map public instance fields - for (Field field : beanClass.getFields()) { - - if (Modifier.isStatic(field.getModifiers())) { - // not a JavaBean property - continue; - } - - final String binding = beanPrefix + preparePropertyName(field.getName()); - final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, field); - - index.put(binding, indexedArg); - - } - - return index; - } - - @SuppressWarnings("unchecked") - private <T> T getAnnotation(Class<T> annotationClass, Annotation [] annotations) { - if (annotations != null) { - for (Annotation annotation : annotations) { - if (annotation.annotationType() == annotationClass) { - return (T) annotation; - } - } - } - return null; - } - - private String preparePropertyName(String value) { - return Character.toLowerCase(value.charAt(0)) + value.substring(1); - } + BindBean bean = getAnnotation(BindBean.class, annotations); + if (bean != null) { + final String prefix = bean.value(); + final Class<?> argumentClass = method.getParameterTypes()[i]; + Map<String, IndexedArgument> beanIndex = buildBeanIndex(i, prefix, argumentClass); + index.putAll(beanIndex); + } + + Class<? extends DataTypeAdapter<?>> typeAdapter = Utils.getDataTypeAdapter(annotations); + final IndexedArgument indexedArgument = new IndexedArgument(i, typeAdapter); + + // :N - 1-indexed, like JDBC ResultSet + index.put("" + (i + 1), indexedArgument); + + // argN - 0-indexed, like Reflection + index.put("arg" + i, indexedArgument); + + // Bound name + Bind binding = getAnnotation(Bind.class, annotations); + if (binding != null && !binding.value().isEmpty()) { + index.put(binding.value(), indexedArgument); + } + + // try mapping Java 8 argument names, may overwrite argN + try { + Class<?> nullArgs = null; + Method getParameters = method.getClass().getMethod("getParameters", nullArgs); + if (getParameters != null) { + Object[] parameters = (Object[]) getParameters.invoke(method, nullArgs); + if (parameters != null) { + Object o = parameters[i]; + Method getName = o.getClass().getMethod("getName", nullArgs); + String j8name = getName.invoke(o, nullArgs).toString(); + if (!j8name.isEmpty()) { + index.put(j8name, indexedArgument); + } + } + } + } catch (Throwable t) { + } + } + + return index; + } + + /** + * Builds an index of parameter name->(position,method) from the method arguments + * array. This index is calculated once per method. + * + * @param argumentIndex + * @param prefix + * @param beanClass + * @return a bindings map of ("prefix.property", IndexedArgument) pairs + */ + private Map<String, IndexedArgument> buildBeanIndex(int argumentIndex, String prefix, Class<?> beanClass) { + + final String beanPrefix = StringUtils.isNullOrEmpty(prefix) ? "" : (prefix + "."); + final Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>(); + + // map JavaBean property getters + for (Method method : beanClass.getMethods()) { + + if (Modifier.isStatic(method.getModifiers()) + || method.getReturnType() == void.class + || method.getParameterTypes().length > 0 + || method.getDeclaringClass() == Object.class) { + + // not a JavaBean property + continue; + } + + final String propertyName; + final String name = method.getName(); + if (name.startsWith("get")) { + propertyName = method.getName().substring(3); + } else if (name.startsWith("is")) { + propertyName = method.getName().substring(2); + } else { + propertyName = null; + } + + if (propertyName == null) { + // not a conventional JavaBean property + continue; + } + + final String binding = beanPrefix + preparePropertyName(propertyName); + final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, method); + + index.put(binding, indexedArg); + } + + // map public instance fields + for (Field field : beanClass.getFields()) { + + if (Modifier.isStatic(field.getModifiers())) { + // not a JavaBean property + continue; + } + + final String binding = beanPrefix + preparePropertyName(field.getName()); + final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, field); + + index.put(binding, indexedArg); + + } + + return index; + } + + @SuppressWarnings("unchecked") + private <T> T getAnnotation(Class<T> annotationClass, Annotation[] annotations) { + if (annotations != null) { + for (Annotation annotation : annotations) { + if (annotation.annotationType() == annotationClass) { + return (T) annotation; + } + } + } + return null; + } + + private String preparePropertyName(String value) { + return Character.toLowerCase(value.charAt(0)) + value.substring(1); + } /* * @@ -541,213 +540,213 @@ final class DaoProxy<X extends Dao> implements InvocationHandler, Dao { * */ - @Override - public final Db db() { - return db; - } - - @Override - public final <T> boolean insert(T t) { - return db.insert(t); - } - - @Override - public final <T> void insertAll(List<T> t) { - db.insertAll(t); - } - - @Override - public final <T> long insertAndGetKey(T t) { - return db.insertAndGetKey(t); - } - - @Override - public final <T> List<Long> insertAllAndGetKeys(List<T> t) { - return db.insertAllAndGetKeys(t); - } - - @Override - public final <T> boolean update(T t) { - return db.update(t); - } - - @Override - public final <T> void updateAll(List<T> t) { - db.updateAll(t); - } - - @Override - public final <T> void merge(T t) { - db.merge(t); - } - - @Override - public final <T> boolean delete(T t) { - return db.delete(t); - } - - @Override - public final <T> void deleteAll(List<T> t) { - db.deleteAll(t); - } - - @Override - public final void close() { - db.close(); - } - - /** - * Container class to hold the prepared JDBC SQL statement and execution - * parameters. - */ - private class PreparedSql { - final String sql; - final Object [] parameters; - - PreparedSql(String sql, Object [] parameters) { - this.sql = sql; - this.parameters = parameters; - } - - @Override - public String toString() { - return sql; - } - - } - - /** - * Container class to hold a parsed JDBC SQL statement and - * IndexedParameters. - * <p> - * Instances of this class are cached because they are functional processing - * containers as they contain Method and Field references for binding beans - * and matching to method arguments. - * </p> - */ - private class IndexedSql { - final String sql; - final List<IndexedArgument> indexedArgs; - - IndexedSql(String sql, List<IndexedArgument> indexedArgs) { - this.sql = sql; - this.indexedArgs = indexedArgs; - } - - /** - * Prepares the method arguments for statement execution. - * - * @param db - * @param methodArgs - * @return the prepared sql statement and parameters - */ - PreparedSql prepareSql(Db db, Object [] methodArgs) { - - Object [] parameters = new Object[indexedArgs.size()]; - - for (int i = 0; i < indexedArgs.size(); i++) { - - IndexedArgument indexedArg = indexedArgs.get(i); - Object methodArg = methodArgs[indexedArg.index]; + @Override + public final Db db() { + return db; + } + + @Override + public final <T> boolean insert(T t) { + return db.insert(t); + } + + @Override + public final <T> void insertAll(List<T> t) { + db.insertAll(t); + } + + @Override + public final <T> long insertAndGetKey(T t) { + return db.insertAndGetKey(t); + } + + @Override + public final <T> List<Long> insertAllAndGetKeys(List<T> t) { + return db.insertAllAndGetKeys(t); + } + + @Override + public final <T> boolean update(T t) { + return db.update(t); + } + + @Override + public final <T> void updateAll(List<T> t) { + db.updateAll(t); + } + + @Override + public final <T> void merge(T t) { + db.merge(t); + } + + @Override + public final <T> boolean delete(T t) { + return db.delete(t); + } + + @Override + public final <T> void deleteAll(List<T> t) { + db.deleteAll(t); + } + + @Override + public final void close() { + db.close(); + } + + /** + * Container class to hold the prepared JDBC SQL statement and execution + * parameters. + */ + private class PreparedSql { + final String sql; + final Object[] parameters; + + PreparedSql(String sql, Object[] parameters) { + this.sql = sql; + this.parameters = parameters; + } + + @Override + public String toString() { + return sql; + } + + } + + /** + * Container class to hold a parsed JDBC SQL statement and + * IndexedParameters. + * <p> + * Instances of this class are cached because they are functional processing + * containers as they contain Method and Field references for binding beans + * and matching to method arguments. + * </p> + */ + private class IndexedSql { + final String sql; + final List<IndexedArgument> indexedArgs; + + IndexedSql(String sql, List<IndexedArgument> indexedArgs) { + this.sql = sql; + this.indexedArgs = indexedArgs; + } + + /** + * Prepares the method arguments for statement execution. + * + * @param db + * @param methodArgs + * @return the prepared sql statement and parameters + */ + PreparedSql prepareSql(Db db, Object[] methodArgs) { + + Object[] parameters = new Object[indexedArgs.size()]; + + for (int i = 0; i < indexedArgs.size(); i++) { + + IndexedArgument indexedArg = indexedArgs.get(i); + Object methodArg = methodArgs[indexedArg.index]; + + Object value = methodArg; + Class<? extends DataTypeAdapter<?>> typeAdapter = indexedArg.typeAdapter; + + if (indexedArg.method != null) { + + // execute the bean method + try { + + value = indexedArg.method.invoke(methodArg); + typeAdapter = Utils.getDataTypeAdapter(indexedArg.method.getAnnotations()); + + } catch (Exception e) { + throw new IciqlException(e); + } + + } else if (indexedArg.field != null) { + + // extract the field value + try { + + value = indexedArg.field.get(methodArg); + typeAdapter = Utils.getDataTypeAdapter(indexedArg.field.getAnnotations()); + + } catch (Exception e) { + throw new IciqlException(e); + } + + } else if (typeAdapter == null) { + + // identify the type adapter for the argument class + typeAdapter = Utils.getDataTypeAdapter(methodArg.getClass().getAnnotations()); + } + + // prepare the parameter + parameters[i] = db.getDialect().serialize(value, typeAdapter); + + } + + return new PreparedSql(sql, parameters); + + } + + @Override + public String toString() { + return sql; + } + } + + /** + * IndexedArgument holds cached information about how to process an method + * argument by it's index in the method arguments array. + * <p> + * An argument may be passed-through, might be bound to a bean property, + * might be transformed with a type adapter, or a combination of these. + * </p> + */ + private class IndexedArgument { + final int index; + final Class<? extends DataTypeAdapter<?>> typeAdapter; + final Method method; + final Field field; + + IndexedArgument(int index, Class<? extends DataTypeAdapter<?>> typeAdapter) { + this.index = index; + this.typeAdapter = typeAdapter; + this.method = null; + this.field = null; + } + + IndexedArgument(int methodArgIndex, Method method) { + this.index = methodArgIndex; + this.typeAdapter = null; + this.method = method; + this.field = null; + } + + IndexedArgument(int methodArgIndex, Field field) { + this.index = methodArgIndex; + this.typeAdapter = null; + this.method = null; + this.field = field; + } + + @Override + public String toString() { + + String accessor; + if (method != null) { + accessor = "M:" + method.getDeclaringClass().getSimpleName() + "." + method.getName(); + } else if (field != null) { + accessor = "F:" + field.getDeclaringClass().getSimpleName() + "." + field.getName(); + } else { + accessor = "A:arg"; + } - Object value = methodArg; - Class<? extends DataTypeAdapter<?>> typeAdapter = indexedArg.typeAdapter; - - if (indexedArg.method != null) { - - // execute the bean method - try { - - value = indexedArg.method.invoke(methodArg); - typeAdapter = Utils.getDataTypeAdapter(indexedArg.method.getAnnotations()); - - } catch (Exception e) { - throw new IciqlException(e); - } - - } else if (indexedArg.field != null) { - - // extract the field value - try { - - value = indexedArg.field.get(methodArg); - typeAdapter = Utils.getDataTypeAdapter(indexedArg.field.getAnnotations()); - - } catch (Exception e) { - throw new IciqlException(e); - } - - } else if (typeAdapter == null) { - - // identify the type adapter for the argument class - typeAdapter = Utils.getDataTypeAdapter(methodArg.getClass().getAnnotations()); - } - - // prepare the parameter - parameters[i] = db.getDialect().serialize(value, typeAdapter); - - } - - return new PreparedSql(sql, parameters); - - } - - @Override - public String toString() { - return sql; - } - } - - /** - * IndexedArgument holds cached information about how to process an method - * argument by it's index in the method arguments array. - * <p> - * An argument may be passed-through, might be bound to a bean property, - * might be transformed with a type adapter, or a combination of these. - * </p> - */ - private class IndexedArgument { - final int index; - final Class<? extends DataTypeAdapter<?>> typeAdapter; - final Method method; - final Field field; - - IndexedArgument(int index, Class<? extends DataTypeAdapter<?>> typeAdapter) { - this.index = index; - this.typeAdapter = typeAdapter; - this.method = null; - this.field = null; - } - - IndexedArgument(int methodArgIndex, Method method) { - this.index = methodArgIndex; - this.typeAdapter = null; - this.method = method; - this.field = null; - } - - IndexedArgument(int methodArgIndex, Field field) { - this.index = methodArgIndex; - this.typeAdapter = null; - this.method = null; - this.field = field; - } - - @Override - public String toString() { - - String accessor; - if (method != null) { - accessor = "M:" + method.getDeclaringClass().getSimpleName() + "." + method.getName(); - } else if (field != null) { - accessor = "F:" + field.getDeclaringClass().getSimpleName() + "." + field.getName(); - } else { - accessor = "A:arg"; - } - - return index + ":" + accessor + (typeAdapter == null ? "" : (":" + typeAdapter.getSimpleName())); - } - - } + return index + ":" + accessor + (typeAdapter == null ? "" : (":" + typeAdapter.getSimpleName())); + } + + } } |