| /* |
| * 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); |
| } |
| } |
| } |
| } |