blob: 4b938735679976d21db651025016329af5bc9afe [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.openjpa.kernel;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.apache.openjpa.enhance.Reflection;
import org.apache.openjpa.lib.util.Localizer;
/**
* A strategy to fill data into a {@link ResultShape}.
* <BR>
* Available strategy implementations can fill by invoking constructor, setting array elements, direct assignment,
* invoking put(key,value)-style method on Map or factory-constructed instance.
*
* @author Pinaki Poddar
*
* @since 2.0.0
*
*/
public interface FillStrategy<T> {
Localizer _loc = Localizer.forPackage(FillStrategy.class);
T fill(Object[] data, Class<?>[] types, String[] aliases);
/**
* Fills an array of given type.
*
* @param <T> must be an array type.
*/
public static class Array<T> implements FillStrategy<T> {
private final Class<?> cls;
public Array(Class<T> arrayCls) {
if (arrayCls == null || !arrayCls.isArray())
throw new IllegalArgumentException(_loc.get("fill-bad-array", arrayCls).getMessage());
this.cls = arrayCls.getComponentType();
}
@Override
public T fill(Object[] values, Class<?>[] types, String[] aliases) {
Object array = java.lang.reflect.Array.newInstance(cls, values.length);
System.arraycopy(values, 0, array, 0, values.length);
return (T)array;
}
}
/**
* Construct and populate an instance by invoking the put method
* with each alias as key and element of the given array of values.
*
* The instance is a created by the no-argument constructor of the declaring class of the given method.
*/
public static class Map<T> implements FillStrategy<T> {
private final Method putMethod;
public Map(Method put) {
if (put == null || put.getParameterTypes().length != 2)
throw new IllegalArgumentException(_loc.get("fill-bad-put", put).getMessage());
putMethod = put;
}
@Override
public T fill(Object[] values, Class<?>[] types, String[] aliases) {
int i = 0;
try {
Object map = putMethod.getDeclaringClass().newInstance();
for (i = 0; i < values.length; i++)
putMethod.invoke(map, aliases[i], values[i]);
return (T)map;
} catch (InvocationTargetException t) {
throw new RuntimeException(_loc.get("fill-map-error", putMethod, aliases[i], values[i]).getMessage(),
t.getTargetException());
} catch (Exception e) {
throw new RuntimeException(_loc.get("fill-map-error", putMethod, aliases[i], values[i]).getMessage(),
e);
}
}
}
/**
* Construct and populate an instance by the given constructor and arguments.
*/
public static class NewInstance<T> implements FillStrategy<T> {
private Constructor<? extends T> cons;
private Class<T> cls;
public NewInstance(Constructor<? extends T> cons) {
this.cons = cons;
}
public NewInstance(Class<T> cls) {
this.cls = cls;
}
/**
* Finds a constructor of the given class with given argument types.
*/
<X> Constructor<X> findConstructor(Class<X> cls, Class<?>[] types) {
try {
return cls.getConstructor(types);
} catch (Exception e) {
Constructor<?>[] constructors = cls.getConstructors();
for (Constructor<?> cons : constructors) {
Class<?>[] paramTypes = cons.getParameterTypes();
boolean match = false;
if (paramTypes.length == types.length) {
for (int i = 0; i < paramTypes.length; i++) {
match = paramTypes[i].isAssignableFrom(Filters.wrap(types[i]));
if (!match)
break;
}
}
if (match) {
return (Constructor<X>)cons;
}
}
}
throw new RuntimeException(_loc.get("fill-ctor-none", cls, Arrays.toString(types)).getMessage());
}
@Override
public T fill(Object[] values, Class<?>[] types, String[] aliases) {
if (cons == null) {
cons = findConstructor(cls, types);
}
try {
for (int i = 0; i < values.length; i++) {
values[i] = Filters.convert(values[i], types[i]);
}
return cons.newInstance(values);
} catch (Exception e) {
throw new RuntimeException(_loc.get("fill-ctor-error", cons, Arrays.toString(values),
Arrays.toString(types)).getMessage(), e);
}
}
}
/**
* Create and populate a bean by invoking setter methods identified by alias name with each array
* element value as argument.
*/
public static class Bean<T> implements FillStrategy<T> {
private final Class<T> cls;
private Method[] setters;
public Bean(Class<T> cls) {
this.cls = cls;
}
@Override
public T fill(Object[] values, Class<?>[] types, String[] aliases) {
int i = 0;
try {
if (setters == null) {
setters = new Method[values.length];
}
T bean = cls.newInstance();
for (i = 0; i < values.length; i++) {
if (setters[i] == null) {
setters[i] = Reflection.findSetter(cls, aliases[i], false);
if (setters[i] == null)
throw new RuntimeException(_loc.get("fill-bean-setter", cls, aliases[i]).getMessage());
}
setters[i].invoke(bean, Filters.convert(values[i], types[i]));
}
return bean;
} catch (InvocationTargetException t) {
throw new RuntimeException(_loc.get("fill-bean-error", setters[i], values[i], types[i]).getMessage(),
t.getTargetException());
} catch (Exception e) {
throw new RuntimeException(_loc.get("fill-bean-error", setters[i], values[i], types[i]).getMessage(),
e);
}
}
}
/**
* Populate an instance by simply assigning the 0-th element of the input values.
*/
public static class Assign<T> implements FillStrategy<T> {
@Override
public T fill(Object[] values, Class<?>[] types, String[] aliases) {
try {
return (T)values[0];
} catch (Exception e) {
throw new RuntimeException(_loc.get("fill-assign-error", Arrays.toString(values),
Arrays.toString(types)).getMessage(), e);
}
}
}
/**
* Populate an instance created by given factory using a given put(key,value) method.
* If the first argument of the given put method is integer then fill the values
* by index else fill the values with alias key.
*/
public static class Factory<T> implements FillStrategy<T> {
final ObjectFactory<T> factory;
final Method putMethod;
final boolean isArray;
public Factory(ObjectFactory<T> factory, Method put) {
this.factory = factory;
this.putMethod = put;
if (put == null || put.getParameterTypes().length != 2)
throw new IllegalArgumentException(_loc.get("fill-factory-bad-put", put).getMessage());
Class<?> keyType = putMethod.getParameterTypes()[0];
this.isArray = keyType == int.class || keyType == Integer.class;
}
@Override
public T fill(Object[] values, Class<?>[] types, String[] aliases) {
int i = 0;
Object key = null;
T result = factory.newInstance();
try {
for (i = 0; i < values.length; i++) {
key = isArray ? i : aliases[i];
putMethod.invoke(result, key, Filters.convert(values[i], types[i]));
}
return result;
} catch (InvocationTargetException t) {
throw new RuntimeException(_loc.get("fill-factory-error", new Object[]{putMethod, key, values[i],
types[i]}).getMessage(), t.getTargetException());
} catch (Exception e) {
throw new RuntimeException(_loc.get("fill-factory-error", new Object[]{putMethod, key, values[i],
types[i]}).getMessage(), e);
}
}
}
}