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    }