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