001/*
002 *  Copyright 2013 Chris Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.reflection.core.misc;
017
018import java.lang.reflect.Field;
019import java.lang.reflect.Method;
020import java.lang.reflect.Modifier;
021import java.math.BigDecimal;
022import java.math.BigInteger;
023import java.net.URI;
024import java.net.URL;
025import java.util.HashSet;
026import java.util.Set;
027import java.util.UUID;
028import java.util.regex.Pattern;
029
030/**
031 * Utility methods for inspecting and interpreting classes
032 */
033public final class ClassUtils {
034    
035    private ClassUtils() {
036    }
037
038    /**
039     * Indicate if the class is a known non-primitive, JDK immutable type
040     * @param type Class to test
041     * @return True if the type is an immutable from the JDK class libraries
042     */
043    public static boolean isJdkImmutable(Class<?> type) {
044        
045        if (Class.class == type) {
046                return true;
047        }
048        if (String.class == type) {
049            return true;
050        }
051        if (BigInteger.class == type) {
052            return true;
053        }
054        if (BigDecimal.class == type) {
055            return true;
056        }
057        if (URL.class == type) {
058            return true;
059        }
060        if (UUID.class == type) {
061            return true;
062        }
063        if (URI.class == type) {
064            return true;
065        }
066        if (Pattern.class == type) {
067            return true;
068        }
069        return false;
070    }
071
072    /**
073     * Indicate if the class is a known primitive wrapper type
074     * @param type Class to test
075     * @return True if the type is a primitive wrapper type
076     */
077    public static boolean isWrapper(Class<?> type) {
078
079        if (Boolean.class == type) {
080            return true;
081        } 
082        if (Byte.class == type) {
083            return true;
084        } 
085        if (Character.class == type) {
086            return true;
087        } 
088        if (Short.class == type) {
089            return true;
090        } 
091        if (Integer.class == type) {
092            return true;
093        } 
094        if (Long.class == type) {
095            return true;
096        }
097        if (Float.class == type) {
098            return true;
099        } 
100        if (Double.class == type) {
101            return true;
102        }
103        return false;
104    }
105    
106    /**
107     * Produces an array with all the instance fields of the specified class
108     * @param c The class specified
109     * @return The array of matched Fields
110     */
111    public static Field[] collectDeclaredInstanceFields(Class<?> c) {
112        
113        return collectInstanceFields(c, c.getSuperclass());
114    }
115    
116    /**
117     * Produces an array with all the instance fields of the specified class
118     * @param c The class specified
119     * @return The array of matched Fields
120     */
121    public static Field[] collectInstanceFields(Class<?> c) {
122        
123        return collectFields(c, 0, Modifier.STATIC);
124    }
125
126    /**
127     * Produces an array with all the instance fields of the specified class
128     * @param c The class specified
129     * @param limitExclusive Fields from this class or higher in the hierarchy will not be returned
130     * @return The array of matched Fields
131     */
132    public static Field[] collectInstanceFields(Class<?> c, Class<?> limitExclusive) {
133        
134        return collectFields(c, 0, Modifier.STATIC, limitExclusive);
135    }
136
137    
138    /**
139         * Produces an array with all the instance fields of the specified class which match the supplied rules
140     * @param c The class specified
141     * @param excludePublic Exclude public fields if true
142     * @param excludeProtected Exclude protected fields if true
143     * @param excludePrivate Exclude private fields if true
144     * @return The array of matched Fields
145     */
146    public static Field[] collectInstanceFields(Class<?> c, boolean excludePublic, boolean excludeProtected, boolean excludePrivate) {
147        
148        int inclusiveModifiers = 0;
149        int exclusiveModifiers = Modifier.STATIC;
150        
151        if (excludePrivate) {
152                exclusiveModifiers += Modifier.PRIVATE;
153        }
154        if (excludePublic) {
155                exclusiveModifiers += Modifier.PUBLIC;
156        }
157        if (excludeProtected) {
158                exclusiveModifiers += Modifier.PROTECTED;
159        }
160        
161        return collectFields(c, inclusiveModifiers, exclusiveModifiers);
162    }
163
164    /**
165     * Produces an array with all the fields of the specified class
166     * @param c The class specified
167     * @return The array of matched Fields
168     */
169    public static Field[] collectDeclaredFields(Class<?> c) {
170        
171        return collectFields(c, c.getSuperclass());
172    }
173
174    /**
175     * Produces an array with all the fields of the specified class
176     * @param c The class specified
177     * @return The array of matched Fields
178     */
179    public static Field[] collectFields(Class<?> c) {
180        
181        return collectFields(c, 0, 0);
182    }
183
184    /**
185     * Produces an array with all the fields of the specified class
186     * @param c The class specified
187     * @param limitExclusive Fields from this class or higher in the hierarchy will not be returned
188     * @return The array of matched Fields
189     */
190    public static Field[] collectFields(Class<?> c, Class<?> limitExclusive) {
191        
192        return collectFields(c, 0, 0, limitExclusive);
193    }
194
195    
196    /**
197         * Produces an array with all the instance fields of the specified class which match the supplied rules
198     * @param c The class specified
199     * @param inclusiveModifiers An int indicating the {@link Modifier}s that may be applied
200     * @param exclusiveModifiers An int indicating the {@link Modifier}s that must be excluded
201     * @return The array of matched Fields
202     */
203    public static Field[] collectFields(Class<?> c, int inclusiveModifiers, int exclusiveModifiers) {
204        
205        return collectFields(c, inclusiveModifiers, exclusiveModifiers, Object.class);
206    }
207    
208    /**
209         * Produces an array with all the instance fields of the specified class which match the supplied rules
210     * @param c The class specified
211     * @param inclusiveModifiers An int indicating the {@link Modifier}s that may be applied
212     * @param exclusiveModifiers An int indicating the {@link Modifier}s that must be excluded
213     * @param limitExclusive Fields from this class or higher in the hierarchy will not be returned
214     * @return The array of matched Fields
215     */
216    public static Field[] collectFields(Class<?> c, int inclusiveModifiers, int exclusiveModifiers, Class<?> limitExclusive) {
217        
218        Set<Field> fields = new HashSet<Field>();
219        while (c != limitExclusive) {
220            for (Field f : c.getDeclaredFields()) {
221                if (((f.getModifiers() & exclusiveModifiers) == 0)
222                                && ((f.getModifiers() & inclusiveModifiers) == inclusiveModifiers)) {
223                    fields.add(f);
224                    f.setAccessible(true);
225                }
226            }
227            c = c.getSuperclass();
228        }
229        return fields.toArray(new Field[]{});
230    }
231
232    /**
233     * Produces an array with all the methods of the specified class
234     * @param c The class specified
235     * @return The array of matched Methods
236     */
237    public static Method[] collectDeclaredMethods(Class<?> c) {
238        return collectMethods(c, 0, 0, c.getSuperclass());
239    }
240
241    
242    /**
243     * Produces an array with all the methods of the specified class
244     * @param c The class specified
245     * @return The array of matched Methods
246     */
247    public static Method[] collectMethods(Class<?> c) {
248        
249        return collectMethods(c, 0, 0);
250    }
251
252    /**
253     * Produces an array with all the methods of the specified class
254     * @param c The class specified
255     * @param limitExclusive Methods from this class or higher in the hierarchy will not be returned
256     * @return The array of matched Methods
257     */
258    public static Method[] collectMethods(Class<?> c, Class<?> limitExclusive) {
259        
260        return collectMethods(c, 0, 0, limitExclusive);
261    }
262
263    
264    /**
265     * Produces an array with all the methods of the specified class
266     * @param c The class specified
267     * @param inclusiveModifiers An int indicating the {@link Modifier}s that may be applied
268     * @param exclusiveModifiers An int indicating the {@link Modifier}s that must be excluded
269     * @return The array of matched Methods
270     */
271    public static Method[] collectMethods(Class<?> c, int inclusiveModifiers, int exclusiveModifiers) {
272    
273        return collectMethods(c, inclusiveModifiers, exclusiveModifiers, Object.class);
274    }
275    
276    /**
277     * Produces an array with all the methods of the specified class
278     * @param c The class specified
279     * @param inclusiveModifiers An int indicating the {@link Modifier}s that may be applied
280     * @param exclusiveModifiers An int indicating the {@link Modifier}s that must be excluded
281     * @param limitExclusive Methods from this class or higher in the hierarchy will not be returned
282     * @return The array of matched Methods
283     */
284    public static Method[] collectMethods(Class<?> c, int inclusiveModifiers, int exclusiveModifiers, Class<?> limitExclusive) {
285        
286        Set<Method> methods = new HashSet<Method>();
287        while (c != limitExclusive) {
288            for (Method f : c.getDeclaredMethods()) {
289                if (((f.getModifiers() & exclusiveModifiers) == 0)
290                                && ((f.getModifiers() & inclusiveModifiers) == inclusiveModifiers)) {
291                    methods.add(f);
292                    f.setAccessible(true);
293                }
294            }
295            c = c.getSuperclass();
296        }
297        return methods.toArray(new Method[]{});
298    }
299}