| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package io.v.v23.vom; |
| |
| import io.v.v23.vdl.VdlArray; |
| import io.v.v23.vdl.VdlComplex128; |
| import io.v.v23.vdl.VdlComplex64; |
| import io.v.v23.vdl.VdlEnum; |
| import io.v.v23.vdl.VdlList; |
| import io.v.v23.vdl.VdlMap; |
| import io.v.v23.vdl.VdlSet; |
| import io.v.v23.vdl.VdlStruct; |
| import io.v.v23.vdl.VdlType; |
| import io.v.v23.vdl.VdlValue; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * ReflectUtil provides helpers to get object properties and create class instances from reflection. |
| */ |
| final class ReflectUtil { |
| /** |
| * Creates an instance of java primitives, one of boolean, byte, short, int, long, float, |
| * double, String. Handles java types and VDL types. |
| * |
| * @param target the target containing java class and VDL type information |
| * @param value the value of primitive to be created |
| * @return an instance of VDL primitive containing the provided value if the target class |
| * is inherited from {@code VdlValue}; returns provided value otherwise |
| * @throws ConversionException if the instance of the target class can't be created |
| */ |
| static Object createPrimitive(ConversionTarget target, Object value, Class<?> valueType) |
| throws ConversionException { |
| Class<?> targetClass = target.getTargetClass(); |
| try { |
| if (targetClass.getSuperclass() == VdlValue.class) { |
| return targetClass.getConstructor(VdlType.class, valueType) |
| .newInstance(target.getVdlType(), value); |
| } else if (VdlValue.class.isAssignableFrom(targetClass)) { |
| return targetClass.getConstructor(valueType).newInstance(value); |
| } else { |
| return value; |
| } |
| } catch (Exception e) { |
| throw new ConversionException(value, targetClass, e); |
| } |
| } |
| |
| /** |
| * Creates an instance of VDL complex. The target class should be inherited from |
| * {@code VdlValue}. |
| */ |
| static VdlValue createComplex(ConversionTarget target, double real, double imag) |
| throws ConversionException { |
| Class<?> targetClass = target.getTargetClass(); |
| try { |
| if (targetClass == VdlComplex64.class) { |
| return new VdlComplex64(target.getVdlType(), (float) real, (float) imag); |
| } else if (targetClass == VdlComplex128.class) { |
| return new VdlComplex128(target.getVdlType(), real, imag); |
| } else if (VdlComplex64.class.isAssignableFrom(targetClass)) { |
| return (VdlValue) targetClass.getConstructor(Float.TYPE, Float.TYPE) |
| .newInstance((float) real, (float) imag); |
| } else if (VdlComplex128.class.isAssignableFrom(targetClass)) { |
| return (VdlValue) targetClass.getConstructor(Double.TYPE, Double.TYPE) |
| .newInstance(real, imag); |
| } |
| } catch (Exception e) { |
| throw new ConversionException( |
| new VdlComplex128(real, imag), targetClass, e); |
| } |
| throw new ConversionException(new VdlComplex128(real, imag), targetClass); |
| } |
| |
| /** |
| * Creates an instance of VDL enum. The target class should be inherited from {@code VdlEnum}. |
| */ |
| static VdlEnum createEnum(ConversionTarget target, String label) throws ConversionException { |
| Class<?> targetClass = target.getTargetClass(); |
| if (targetClass == VdlEnum.class) { |
| return new VdlEnum(target.getVdlType(), label); |
| } |
| try { |
| return (VdlEnum) targetClass.getMethod("valueOf", String.class) |
| .invoke(null, label); |
| } catch (Exception e) { |
| throw new ConversionException(label, targetClass, e); |
| } |
| } |
| |
| private static Object createNamedGeneric(Class<?> targetClass, Object impl) |
| throws ConversionException { |
| try { |
| if (VdlArray.class.isAssignableFrom(targetClass)) { |
| return construct(targetClass, impl.getClass(), impl); |
| } else if (VdlList.class.isAssignableFrom(targetClass)) { |
| return construct(targetClass, List.class, impl); |
| } else if (VdlMap.class.isAssignableFrom(targetClass)) { |
| return construct(targetClass, Map.class, impl); |
| } else if (VdlSet.class.isAssignableFrom(targetClass)) { |
| return construct(targetClass, Set.class, impl); |
| } |
| } catch (Exception e) { |
| throw new ConversionException(impl, targetClass, e); |
| } |
| throw new ConversionException(impl, targetClass); |
| } |
| |
| private static Object construct( |
| Class<?> targetClass, Class<?> ctorParamType, Object... ctorArgs) |
| throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, |
| InstantiationException { |
| Constructor<?> ctor = targetClass.getConstructor(ctorParamType); |
| ctor.setAccessible(true); |
| return ctor.newInstance(ctorArgs); |
| } |
| |
| /** |
| * Creates an instance of generic type, one of array, list, map, set. |
| * |
| * @param target the target containing java class and VDL type information |
| * @param impl the implementation of generic type to be created |
| * @return an instance of VDL generic containing wrapped around the provided implementation |
| * object if the target class is inherited from {@code VdlValue}; returns provided |
| * implementation object otherwise |
| * @throws ConversionException if the instance of the target class can't be created |
| */ |
| @SuppressWarnings("unchecked") |
| static Object createGeneric(ConversionTarget target, Object impl) throws ConversionException { |
| Class<?> targetClass = target.getTargetClass(); |
| if (targetClass == VdlArray.class) { |
| return new VdlArray<Object>(target.getVdlType(), (Object[]) impl); |
| } else if (targetClass == VdlList.class) { |
| return new VdlList<Object>(target.getVdlType(), (List<Object>) impl); |
| } else if (targetClass == VdlSet.class) { |
| return new VdlSet<Object>(target.getVdlType(), (Set<Object>) impl); |
| } else if (targetClass == VdlMap.class) { |
| return new VdlMap<Object, Object>(target.getVdlType(), (Map<Object, Object>) impl); |
| } else if (VdlValue.class.isAssignableFrom(targetClass)) { |
| return createNamedGeneric(targetClass, impl); |
| } else { |
| return impl; |
| } |
| } |
| |
| /** |
| * Creates an instance of VDL struct. The target class should be inherited from |
| * {@code AbstractVdlStruct}. |
| */ |
| static Object createStruct(ConversionTarget target) throws ConversionException { |
| Class<?> targetClass = target.getTargetClass(); |
| if (targetClass == VdlStruct.class) { |
| return new VdlStruct(target.getVdlType()); |
| } |
| try { |
| Constructor<?> ctor = targetClass.getConstructor(); |
| ctor.setAccessible(true); |
| return ctor.newInstance(); |
| } catch (Exception e) { |
| throw new ConversionException(target.getVdlType(), targetClass, e); |
| } |
| } |
| |
| /** |
| * Returns a {@code Class} object that is represented by provided {@code Type} object. |
| */ |
| static Class<?> getRawClass(Type type) { |
| if (type instanceof Class) { |
| return (Class<?>) type; |
| } else if (type instanceof ParameterizedType) { |
| return getRawClass(((ParameterizedType) type).getRawType()); |
| } else if (type instanceof GenericArrayType) { |
| Class<?> component = getRawClass(((GenericArrayType) type).getGenericComponentType()); |
| return Array.newInstance(component, 0).getClass(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Returns type of element at provided index for generic or array type. |
| * |
| * @param type the generic type |
| * @throws ConversionException if the type has no element at index |
| * @returns an array of element types; the returned array is empty if the provided type |
| * is not generic or array |
| */ |
| static Type getElementType(Type type, int index) throws ConversionException { |
| Type[] types = new Type[0]; |
| if (type instanceof Class) { |
| Class<?> klass = (Class<?>) type; |
| if (klass.isArray()) { |
| types = new Type[]{klass.getComponentType()}; |
| } else { |
| return getElementType(klass.getGenericSuperclass(), index); |
| } |
| } else if (type instanceof ParameterizedType) { |
| types = ((ParameterizedType) type).getActualTypeArguments(); |
| } else if (type instanceof GenericArrayType) { |
| types = new Type[]{((GenericArrayType) type).getGenericComponentType()}; |
| } |
| if (index < 0 || index >= types.length) { |
| throw new ConversionException("Type " + type + " has no element at index " + index); |
| } |
| return types[index]; |
| } |
| |
| /** |
| * Sets the value of the indexed element of the specified array object to the specified value. |
| * |
| * @param array the array |
| * @param index the index into the array |
| * @param value new value of the indexed element |
| * @param elementClass the class of elements in the array |
| */ |
| static void setArrayValue(Object array, int index, Object value, Class<?> elementClass) { |
| if (elementClass == Boolean.TYPE) { |
| Array.setBoolean(array, index, (Boolean) value); |
| } else if (elementClass == Byte.TYPE) { |
| Array.setByte(array, index, (Byte) value); |
| } else if (elementClass == Double.TYPE) { |
| Array.setDouble(array, index, (Double) value); |
| } else if (elementClass == Float.TYPE) { |
| Array.setFloat(array, index, (Float) value); |
| } else if (elementClass == Integer.TYPE) { |
| Array.setInt(array, index, (Integer) value); |
| } else if (elementClass == Long.TYPE) { |
| Array.setLong(array, index, (Long) value); |
| } else if (elementClass == Short.TYPE) { |
| Array.setShort(array, index, (Short) value); |
| } else { |
| Array.set(array, index, value); |
| } |
| } |
| } |