| /** |
| * 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.xbean.recipe; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.xbean.propertyeditor.PropertyEditors; |
| |
| /** |
| * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $ |
| */ |
| public final class RecipeHelper { |
| private RecipeHelper() { |
| } |
| |
| public static Recipe getCaller() { |
| LinkedList<Recipe> stack = ExecutionContext.getContext().getStack(); |
| if (stack.size() < 2) { |
| return null; |
| } |
| return stack.get(stack.size() - 2); |
| } |
| |
| public static Class loadClass(String name) throws ClassNotFoundException { |
| ClassLoader classLoader = ExecutionContext.getContext().getClassLoader(); |
| Class<?> type = Class.forName(name, true, classLoader); |
| return type; |
| } |
| |
| public static boolean hasDefaultConstructor(Class type) { |
| if (!Modifier.isPublic(type.getModifiers())) { |
| return false; |
| } |
| if (Modifier.isAbstract(type.getModifiers())) { |
| return false; |
| } |
| Constructor[] constructors = type.getConstructors(); |
| for (Constructor constructor : constructors) { |
| if (Modifier.isPublic(constructor.getModifiers()) && |
| constructor.getParameterTypes().length == 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static boolean isSimpleType(Object o) { |
| return o == null || |
| o instanceof Boolean || |
| o instanceof Character || |
| o instanceof Byte || |
| o instanceof Short || |
| o instanceof Integer || |
| o instanceof Long || |
| o instanceof Float || |
| o instanceof Double || |
| o instanceof String || |
| o instanceof Recipe; |
| |
| } |
| |
| public static <K,V> List<Map.Entry<K,V>> prioritizeProperties(Map<K,V> properties) { |
| ArrayList<Map.Entry<K,V>> entries = new ArrayList<Map.Entry<K,V>>(properties.entrySet()); |
| Collections.sort(entries, new RecipeComparator()); |
| return entries; |
| } |
| |
| public static boolean isInstance(Type t, Object instance) { |
| Class type = toClass(t); |
| if (type.isPrimitive()) { |
| // for primitives the insance can't be null |
| if (instance == null) { |
| return false; |
| } |
| |
| // verify instance is the correct wrapper type |
| if (type.equals(boolean.class)) { |
| return instance instanceof Boolean; |
| } else if (type.equals(char.class)) { |
| return instance instanceof Character; |
| } else if (type.equals(byte.class)) { |
| return instance instanceof Byte; |
| } else if (type.equals(short.class)) { |
| return instance instanceof Short; |
| } else if (type.equals(int.class)) { |
| return instance instanceof Integer; |
| } else if (type.equals(long.class)) { |
| return instance instanceof Long; |
| } else if (type.equals(float.class)) { |
| return instance instanceof Float; |
| } else if (type.equals(double.class)) { |
| return instance instanceof Double; |
| } else { |
| throw new AssertionError("Invalid primitve type: " + type); |
| } |
| } |
| |
| return instance == null || type.isInstance(instance); |
| } |
| |
| public static boolean isConvertable(Type type, Object propertyValue) { |
| if (propertyValue instanceof Recipe) { |
| Recipe recipe = (Recipe) propertyValue; |
| return recipe.canCreate(type); |
| } |
| return (propertyValue instanceof String && PropertyEditors.canConvert(toClass(type))) |
| || (type == String.class && char[].class.isInstance(propertyValue)); |
| } |
| |
| public static boolean isAssignableFrom(Class expected, Class actual) { |
| if (expected == null) return true; |
| |
| if (expected.isPrimitive()) { |
| // verify actual is the correct wrapper type |
| if (expected.equals(boolean.class)) { |
| return actual.equals(Boolean.class); |
| } else if (expected.equals(char.class)) { |
| return actual.equals(Character.class); |
| } else if (expected.equals(byte.class)) { |
| return actual.equals(Byte.class); |
| } else if (expected.equals(short.class)) { |
| return actual.equals(Short.class); |
| } else if (expected.equals(int.class)) { |
| return actual.equals(Integer.class); |
| } else if (expected.equals(long.class)) { |
| return actual.equals(Long.class); |
| } else if (expected.equals(float.class)) { |
| return actual.equals(Float.class); |
| } else if (expected.equals(double.class)) { |
| return actual.equals(Double.class); |
| } else { |
| throw new AssertionError("Invalid primitve type: " + expected); |
| } |
| } |
| |
| return expected.isAssignableFrom(actual); |
| } |
| |
| public static Object convert(Type expectedType, Object value, boolean lazyRefAllowed) { |
| if (value instanceof Recipe) { |
| Recipe recipe = (Recipe) value; |
| value = recipe.create(expectedType, lazyRefAllowed); |
| } |
| |
| // some shortcuts for common string operations |
| if (char[].class == expectedType && String.class.isInstance(value)) { |
| return String.class.cast(value).toCharArray(); |
| } |
| if (String.class == expectedType && char[].class.isInstance(value)) { |
| return new String(char[].class.cast(value)); |
| } |
| |
| if (value instanceof String && (expectedType != Object.class)) { |
| String stringValue = (String) value; |
| value = PropertyEditors.getValue(expectedType, stringValue); |
| } |
| return value; |
| } |
| |
| public static boolean isAssignableFrom(List<? extends Class<?>> expectedTypes, List<? extends Class<?>> actualTypes) { |
| if (expectedTypes.size() != actualTypes.size()) { |
| return false; |
| } |
| for (int i = 0; i < expectedTypes.size(); i++) { |
| Class expectedType = expectedTypes.get(i); |
| Class actualType = actualTypes.get(i); |
| if (expectedType != actualType && !isAssignableFrom(expectedType, actualType)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public static boolean isAssignable(Type expectedType, Type actualType) { |
| Class expectedClass = toClass(expectedType); |
| Class actualClass = toClass(actualType); |
| return expectedClass.isAssignableFrom(actualClass); |
| } |
| |
| public static Class toClass(Type type) { |
| // GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType |
| if (type instanceof Class) { |
| Class clazz = (Class) type; |
| return clazz; |
| } else if (type instanceof GenericArrayType) { |
| GenericArrayType arrayType = (GenericArrayType) type; |
| Class componentType = toClass(arrayType.getGenericComponentType()); |
| return Array.newInstance(componentType, 0).getClass(); |
| } else if (type instanceof ParameterizedType) { |
| ParameterizedType parameterizedType = (ParameterizedType) type; |
| return toClass(parameterizedType.getRawType()); |
| } else { |
| return Object.class; |
| } |
| } |
| |
| public static class RecipeComparator implements Comparator<Object> { |
| public int compare(Object left, Object right) { |
| if (!(left instanceof Recipe) && !(right instanceof Recipe)) return 0; |
| if (left instanceof Recipe && !(right instanceof Recipe)) return 1; |
| if (!(left instanceof Recipe) && right instanceof Recipe) return -1; |
| |
| float leftPriority = ((Recipe) left).getPriority(); |
| float rightPriority = ((Recipe) right).getPriority(); |
| |
| if (leftPriority > rightPriority) return 1; |
| if (leftPriority < rightPriority) return -1; |
| return 0; |
| } |
| } |
| |
| public static Type[] getTypeParameters(Class desiredType, Type type) { |
| if (type instanceof Class) { |
| Class rawClass = (Class) type; |
| |
| // if this is the collection class we're done |
| if (desiredType.equals(type)) { |
| return null; |
| } |
| |
| for (Type intf : rawClass.getGenericInterfaces()) { |
| Type[] collectionType = getTypeParameters(desiredType, intf); |
| if (collectionType != null) { |
| return collectionType; |
| } |
| } |
| |
| Type[] collectionType = getTypeParameters(desiredType, rawClass.getGenericSuperclass()); |
| return collectionType; |
| } else if (type instanceof ParameterizedType) { |
| ParameterizedType parameterizedType = (ParameterizedType) type; |
| |
| Type rawType = parameterizedType.getRawType(); |
| if (desiredType.equals(rawType)) { |
| Type[] argument = parameterizedType.getActualTypeArguments(); |
| return argument; |
| } |
| Type[] collectionTypes = getTypeParameters(desiredType,rawType); |
| if (collectionTypes != null) { |
| for (int i = 0; i < collectionTypes.length; i++) { |
| if (collectionTypes[i] instanceof TypeVariable) { |
| TypeVariable typeVariable = (TypeVariable) collectionTypes[i]; |
| TypeVariable[] rawTypeParams = ((Class) rawType).getTypeParameters(); |
| for (int j = 0; j < rawTypeParams.length; j++) { |
| if (typeVariable.getName().equals(rawTypeParams[j].getName())) { |
| collectionTypes[i] = parameterizedType.getActualTypeArguments()[j]; |
| } |
| } |
| } |
| } |
| } |
| return collectionTypes; |
| } |
| return null; |
| } |
| } |