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.PropertyEditors;
034
035/**
036 * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $
037 */
038public 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}