001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.xbean.recipe; 018 019import java.lang.reflect.Array; 020import java.lang.reflect.Constructor; 021import java.lang.reflect.GenericArrayType; 022import java.lang.reflect.Modifier; 023import java.lang.reflect.ParameterizedType; 024import java.lang.reflect.Type; 025import java.lang.reflect.TypeVariable; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.Comparator; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032 033import org.apache.xbean.propertyeditor.Primitives; 034import org.apache.xbean.propertyeditor.PropertyEditorRegistry; 035import org.apache.xbean.propertyeditor.PropertyEditors; 036 037/** 038 * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $ 039 */ 040public final class RecipeHelper { 041 private RecipeHelper() { 042 } 043 044 public static Recipe getCaller() { 045 LinkedList<Recipe> stack = ExecutionContext.getContext().getStack(); 046 if (stack.size() < 2) { 047 return null; 048 } 049 return stack.get(stack.size() - 2); 050 } 051 052 public static Class loadClass(String name) throws ClassNotFoundException { 053 ClassLoader classLoader = ExecutionContext.getContext().getClassLoader(); 054 Class<?> type = Class.forName(name, true, classLoader); 055 return type; 056 } 057 058 public static boolean hasDefaultConstructor(Class type) { 059 if (!Modifier.isPublic(type.getModifiers())) { 060 return false; 061 } 062 if (Modifier.isAbstract(type.getModifiers())) { 063 return false; 064 } 065 Constructor[] constructors = type.getConstructors(); 066 for (Constructor constructor : constructors) { 067 if (Modifier.isPublic(constructor.getModifiers()) && 068 constructor.getParameterTypes().length == 0) { 069 return true; 070 } 071 } 072 return false; 073 } 074 075 public static boolean isSimpleType(Object o) { 076 return o == null || 077 o instanceof Boolean || 078 o instanceof Character || 079 o instanceof Byte || 080 o instanceof Short || 081 o instanceof Integer || 082 o instanceof Long || 083 o instanceof Float || 084 o instanceof Double || 085 o instanceof String || 086 o instanceof Recipe; 087 088 } 089 090 public static <K,V> List<Map.Entry<K,V>> prioritizeProperties(Map<K,V> properties) { 091 ArrayList<Map.Entry<K,V>> entries = new ArrayList<Map.Entry<K,V>>(properties.entrySet()); 092 Collections.sort(entries, new RecipeComparator()); 093 return entries; 094 } 095 096 public static boolean isInstance(Type t, Object instance) { 097 Class type = toClass(t); 098 if (type.isPrimitive()) { 099 // for primitives the insance can't be null 100 if (instance == null) { 101 return false; 102 } 103 104 // verify instance is the correct wrapper type 105 if (type.equals(boolean.class)) { 106 return instance instanceof Boolean; 107 } else if (type.equals(char.class)) { 108 return instance instanceof Character; 109 } else if (type.equals(byte.class)) { 110 return instance instanceof Byte; 111 } else if (type.equals(short.class)) { 112 return instance instanceof Short; 113 } else if (type.equals(int.class)) { 114 return instance instanceof Integer; 115 } else if (type.equals(long.class)) { 116 return instance instanceof Long; 117 } else if (type.equals(float.class)) { 118 return instance instanceof Float; 119 } else if (type.equals(double.class)) { 120 return instance instanceof Double; 121 } else { 122 throw new AssertionError("Invalid primitve type: " + type); 123 } 124 } 125 126 return instance == null || type.isInstance(instance); 127 } 128 129 public static boolean isConvertable(Type type, Object propertyValue, PropertyEditorRegistry registry) { 130 if (propertyValue instanceof Recipe) { 131 Recipe recipe = (Recipe) propertyValue; 132 return recipe.canCreate(type); 133 } 134 return (propertyValue instanceof String && (registry == null ? PropertyEditors.registry() : registry).findConverter(toClass(type)) != null) 135 || (type == String.class && char[].class.isInstance(propertyValue)); 136 } 137 138 public static boolean isAssignableFrom(Class expected, Class actual) { 139 if (expected == null) return true; 140 141 if (expected.isPrimitive()) { 142 // verify actual is the correct wrapper type 143 return actual.equals(Primitives.toWrapper(expected)); 144 } 145 146 return expected.isAssignableFrom(actual); 147 } 148 149 public static Object convert(Type expectedType, Object value, boolean lazyRefAllowed, PropertyEditorRegistry registry) { 150 if (value instanceof Recipe) { 151 Recipe recipe = (Recipe) value; 152 value = recipe.create(expectedType, lazyRefAllowed); 153 } 154 155 // some shortcuts for common string operations 156 if (char[].class == expectedType && String.class.isInstance(value)) { 157 return String.class.cast(value).toCharArray(); 158 } 159 if (String.class == expectedType && char[].class.isInstance(value)) { 160 return new String(char[].class.cast(value)); 161 } 162 163 if (value instanceof String && (expectedType != Object.class)) { 164 String stringValue = (String) value; 165 value = (registry == null ? PropertyEditors.registry() : registry).getValue(expectedType, stringValue); 166 } 167 return value; 168 } 169 170 public static boolean isAssignableFrom(List<? extends Class<?>> expectedTypes, List<? extends Class<?>> actualTypes) { 171 if (expectedTypes.size() != actualTypes.size()) { 172 return false; 173 } 174 for (int i = 0; i < expectedTypes.size(); i++) { 175 Class expectedType = expectedTypes.get(i); 176 Class actualType = actualTypes.get(i); 177 if (expectedType != actualType && !isAssignableFrom(expectedType, actualType)) { 178 return false; 179 } 180 } 181 return true; 182 } 183 184 public static boolean isAssignable(Type expectedType, Type actualType) { 185 Class expectedClass = toClass(expectedType); 186 Class actualClass = toClass(actualType); 187 return expectedClass.isAssignableFrom(actualClass); 188 } 189 190 public static Class toClass(Type type) { 191 // GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType 192 if (type instanceof Class) { 193 Class clazz = (Class) type; 194 return clazz; 195 } else if (type instanceof GenericArrayType) { 196 GenericArrayType arrayType = (GenericArrayType) type; 197 Class componentType = toClass(arrayType.getGenericComponentType()); 198 return Array.newInstance(componentType, 0).getClass(); 199 } else if (type instanceof ParameterizedType) { 200 ParameterizedType parameterizedType = (ParameterizedType) type; 201 return toClass(parameterizedType.getRawType()); 202 } 203 return Object.class; 204 } 205 206 public static class RecipeComparator implements Comparator<Object> { 207 public int compare(Object left, Object right) { 208 if (!(left instanceof Recipe) && !(right instanceof Recipe)) return 0; 209 if (left instanceof Recipe && !(right instanceof Recipe)) return 1; 210 if (!(left instanceof Recipe) && right instanceof Recipe) return -1; 211 212 float leftPriority = ((Recipe) left).getPriority(); 213 float rightPriority = ((Recipe) right).getPriority(); 214 215 if (leftPriority > rightPriority) return 1; 216 if (leftPriority < rightPriority) return -1; 217 return 0; 218 } 219 } 220 221 public static Type[] getTypeParameters(Class desiredType, Type type) { 222 if (type instanceof Class) { 223 Class rawClass = (Class) type; 224 225 // if this is the collection class we're done 226 if (desiredType.equals(type)) { 227 return null; 228 } 229 230 for (Type intf : rawClass.getGenericInterfaces()) { 231 Type[] collectionType = getTypeParameters(desiredType, intf); 232 if (collectionType != null) { 233 return collectionType; 234 } 235 } 236 237 Type[] collectionType = getTypeParameters(desiredType, rawClass.getGenericSuperclass()); 238 return collectionType; 239 } else if (type instanceof ParameterizedType) { 240 ParameterizedType parameterizedType = (ParameterizedType) type; 241 242 Type rawType = parameterizedType.getRawType(); 243 if (desiredType.equals(rawType)) { 244 Type[] argument = parameterizedType.getActualTypeArguments(); 245 return argument; 246 } 247 Type[] collectionTypes = getTypeParameters(desiredType,rawType); 248 if (collectionTypes != null) { 249 for (int i = 0; i < collectionTypes.length; i++) { 250 if (collectionTypes[i] instanceof TypeVariable) { 251 TypeVariable typeVariable = (TypeVariable) collectionTypes[i]; 252 TypeVariable[] rawTypeParams = ((Class) rawType).getTypeParameters(); 253 for (int j = 0; j < rawTypeParams.length; j++) { 254 if (typeVariable.getName().equals(rawTypeParams[j].getName())) { 255 collectionTypes[i] = parameterizedType.getActualTypeArguments()[j]; 256 } 257 } 258 } 259 } 260 } 261 return collectionTypes; 262 } 263 return null; 264 } 265}