001    /*
002    $Id: ClassHelper.java 4215 2006-11-13 11:18:35Z blackdrag $ created on 25.10.2005
003    
004    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006    Redistribution and use of this software and associated documentation
007    ("Software"), with or without modification, are permitted provided
008    that the following conditions are met:
009    
010    1. Redistributions of source code must retain copyright
011       statements and notices.  Redistributions must also contain a
012       copy of this document.
013    
014    2. Redistributions in binary form must reproduce the
015       above copyright notice, this list of conditions and the
016       following disclaimer in the documentation and/or other
017       materials provided with the distribution.
018    
019    3. The name "groovy" must not be used to endorse or promote
020       products derived from this Software without prior written
021       permission of The Codehaus.  For written permission,
022       please contact info@codehaus.org.
023    
024    4. Products derived from this Software may not be called "groovy"
025       nor may "groovy" appear in their names without prior written
026       permission of The Codehaus. "groovy" is a registered
027       trademark of The Codehaus.
028    
029    5. Due credit should be given to The Codehaus -
030       http://groovy.codehaus.org/
031    
032    THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033    ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034    NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036    THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043    OF THE POSSIBILITY OF SUCH DAMAGE.
044    */
045    
046    package org.codehaus.groovy.ast;
047    
048    import groovy.lang.Closure;
049    import groovy.lang.GString;
050    import groovy.lang.MetaClass;
051    import groovy.lang.Range;
052    import groovy.lang.Reference;
053    import groovy.lang.Script;
054    
055    import java.math.BigDecimal;
056    import java.math.BigInteger;
057    import java.util.List;
058    import java.util.Map;
059    import java.util.regex.Pattern;
060    
061    import org.objectweb.asm.Opcodes;
062    
063    /**
064     * This class is a Helper for ClassNode and classes handling ClassNodes.
065     * It does contain a set of predefined ClassNodes for the most used 
066     * types and some code for cached ClassNode creation and basic 
067     * ClassNode handling 
068     * 
069     * @author Jochen Theodorou
070     */
071    public class ClassHelper {
072        
073    
074        private static String[] names = new String[] {
075            boolean.class.getName(),    char.class.getName(), 
076            byte.class.getName(),       short.class.getName(),
077            int.class.getName(),        long.class.getName(),
078            double.class.getName(),     float.class.getName(),
079            Object.class.getName(),     Void.TYPE.getName(),
080            Closure.class.getName(),    GString.class.getName(),
081            List.class.getName(),       Map.class.getName(),
082            Range.class.getName(),      Pattern.class.getName(),
083            Script.class.getName(),     String.class.getName(),
084            Boolean.class.getName(),    Character.class.getName(),
085            Byte.class.getName(),       Short.class.getName(),
086            Integer.class.getName(),    Long.class.getName(),
087            Double.class.getName(),     Float.class.getName(),
088            BigDecimal.class.getName(), BigInteger.class.getName(),
089            Void.class.getName(),       Reference.class.getName(),
090            Class.class.getName(),      MetaClass.class.getName()
091        };
092        
093        private static Class[] classes = new Class[] {
094            Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
095            Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE,
096            Closure.class, GString.class, List.class, Map.class, Range.class,
097            Pattern.class, Script.class, String.class,  Boolean.class, 
098            Character.class, Byte.class, Short.class, Integer.class, Long.class,
099            Double.class, Float.class, BigDecimal.class, BigInteger.class, Void.class,
100            Reference.class, Class.class, MetaClass.class
101        };
102        
103        public static final ClassNode 
104            DYNAMIC_TYPE = new ClassNode(Object.class),  OBJECT_TYPE = DYNAMIC_TYPE,
105            VOID_TYPE = new ClassNode(Void.TYPE),        CLOSURE_TYPE = new ClassNode(Closure.class),
106            GSTRING_TYPE = new ClassNode(GString.class), LIST_TYPE = new ClassNode(List.class),
107            MAP_TYPE = new ClassNode(Map.class),         RANGE_TYPE = new ClassNode(Range.class),
108            PATTERN_TYPE = new ClassNode(Pattern.class), STRING_TYPE = new ClassNode(String.class),
109            SCRIPT_TYPE = new ClassNode(Script.class),   REFERENCE_TYPE = new ClassNode(Reference.class),
110            
111            boolean_TYPE = new ClassNode(boolean.class),     char_TYPE = new ClassNode(char.class),
112            byte_TYPE = new ClassNode(byte.class),           int_TYPE = new ClassNode(int.class),
113            long_TYPE = new ClassNode(long.class),           short_TYPE = new ClassNode(short.class),
114            double_TYPE = new ClassNode(double.class),       float_TYPE = new ClassNode(float.class),
115            Byte_TYPE = new ClassNode(Byte.class),           Short_TYPE = new ClassNode(Short.class),
116            Integer_TYPE = new ClassNode(Integer.class),     Long_TYPE = new ClassNode(Long.class),
117            Character_TYPE = new ClassNode(Character.class), Float_TYPE = new ClassNode(Float.class),
118            Double_TYPE = new ClassNode(Double.class),       Boolean_TYPE = new ClassNode(Boolean.class),
119            BigInteger_TYPE =  new ClassNode(java.math.BigInteger.class),
120            BigDecimal_TYPE = new ClassNode(java.math.BigDecimal.class),
121            void_WRAPPER_TYPE = new ClassNode(Void.class),   
122            
123            CLASS_Type = new ClassNode(Class.class),        METACLASS_TYPE = new ClassNode(MetaClass.class);
124            
125        
126        private static ClassNode[] types = new ClassNode[] {
127            OBJECT_TYPE,
128            boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE,
129            int_TYPE, long_TYPE, double_TYPE, float_TYPE,
130            VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE,
131            LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE,
132            SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE,
133            Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE,
134            Double_TYPE, Float_TYPE, BigDecimal_TYPE, BigInteger_TYPE, 
135            void_WRAPPER_TYPE, REFERENCE_TYPE, CLASS_Type, METACLASS_TYPE
136        };
137    
138        
139        private static ClassNode[] numbers = new ClassNode[] {
140            char_TYPE, byte_TYPE, short_TYPE, int_TYPE, long_TYPE, 
141            double_TYPE, float_TYPE, Short_TYPE, Byte_TYPE, Character_TYPE,
142            Integer_TYPE, Float_TYPE, Long_TYPE, Double_TYPE, BigInteger_TYPE,
143            BigDecimal_TYPE
144        };
145    
146        protected static final ClassNode[] EMPTY_TYPE_ARRAY = {};
147        
148        public static final String OBJECT = "java.lang.Object";    
149        
150        
151        /**
152         * Creates an array of ClassNodes using an array of classes.
153         * For each of the given classes a new ClassNode will be 
154         * created
155         * @see #make(Class)
156         * @param classes an array of classes used to create the ClassNodes
157         * @return an array of ClassNodes
158         */
159        public static ClassNode[] make(Class[] classes) {
160            ClassNode[] cns = new ClassNode[classes.length];
161            for (int i=0; i<cns.length; i++) {
162                    cns[i] = make(classes[i]);
163            }
164            
165            return cns;
166        }
167        
168        /**
169         * Creates a ClassNode using a given class.
170         * A new ClassNode object is only created if the class
171         * is not one of the predefined ones
172         * 
173         * @param c class used to created the ClassNode
174         * @return ClassNode instance created from the given class
175         */
176        public static ClassNode make(Class c) {
177            for (int i=0; i<classes.length; i++) {
178                if (c==classes[i]) return types[i];
179            }
180            if (c.isArray()) {
181                ClassNode cn = make(c.getComponentType());
182                return cn.makeArray();
183            }
184            ClassNode t = new ClassNode(c);
185            return t;
186        }
187        
188        /**
189         * Creates a ClassNode using a given class.
190         * Unlike make(String) this method will not use the cache
191         * to create the ClassNode. This means the ClassNode created
192         * from this method using the same name will have a different
193         * references
194         * 
195         * @see #make(String)
196         * @param name of the class the ClassNode is representing
197         */
198        public static ClassNode makeWithoutCaching(String name) { 
199            ClassNode cn = new ClassNode(name,Opcodes.ACC_PUBLIC,OBJECT_TYPE);
200            cn.isPrimaryNode = false;
201            return cn;
202        }
203        
204        /**
205         * Creates a ClassNode using a given class.
206         * If the name is one of the predefined ClassNodes then the 
207         * corresponding ClassNode instance will be returned. If the
208         * is null of of length 0 the dynamic type is returned
209         * 
210         * @param name of the class the ClassNode is representing
211         */
212        public static ClassNode make(String name) {
213            if (name == null || name.length() == 0) return DYNAMIC_TYPE;
214            
215            for (int i=0; i<classes.length; i++) {
216                String cname = classes[i].getName();
217                if (name.equals(cname)) return types[i];
218            }        
219            return makeWithoutCaching(name);
220        }
221        
222        /**
223         * Creates a ClassNode containing the wrapper of a ClassNode 
224         * of primitive type. Any ClassNode representing a primitive
225         * type should be created using the predefined types used in
226         * class. The method will check the parameter for known 
227         * references of ClassNode representing a primitive type. If
228         * Reference is found, then a ClassNode will be contained that
229         * represents the wrapper class. For exmaple for boolean, the 
230         * wrapper class is java.lang.Boolean.
231         * 
232         * If the parameter is no primitve type, the redirected 
233         * ClassNode will be returned 
234         *   
235         * @see #make(Class)
236         * @see #make(String)
237         * @param cn the ClassNode containing a possible primitive type
238         */
239        public static ClassNode getWrapper(ClassNode cn) {
240            cn = cn.redirect();
241            if (!isPrimitiveType(cn)) return cn;
242            if (cn==boolean_TYPE) {
243                return Boolean_TYPE;
244            } else if (cn==byte_TYPE) {
245                return Byte_TYPE;
246            } else if (cn==char_TYPE) {
247                return Character_TYPE;
248            } else if (cn==short_TYPE) {
249                return Short_TYPE;
250            } else if (cn==int_TYPE) {
251                return Integer_TYPE;
252            } else if (cn==long_TYPE) {
253                return Long_TYPE;
254            } else if (cn==float_TYPE) {
255                return Float_TYPE;
256            } else if (cn==double_TYPE) {
257                return Double_TYPE;
258            } else if (cn==VOID_TYPE) {
259                    return void_WRAPPER_TYPE;
260            }
261            else {
262                return cn;
263            }
264        }
265        
266        /**
267         * Test to determine if a ClasNode is a primitve type. 
268         * Note: this only works for ClassNodes created using a
269         * predefined ClassNode
270         * 
271         * @see #make(Class)
272         * @see #make(String)
273         * @param cn the ClassNode containing a possible primitive type
274         * @return true if the ClassNode is a primitve type
275         */
276        public static boolean isPrimitiveType(ClassNode cn) {
277            return  cn == boolean_TYPE ||
278                    cn == char_TYPE ||
279                    cn == byte_TYPE ||
280                    cn == short_TYPE ||
281                    cn == int_TYPE ||
282                    cn == long_TYPE ||
283                    cn == float_TYPE ||
284                    cn == double_TYPE ||
285                    cn == VOID_TYPE;
286        }
287    
288        public static ClassNode makeReference() {
289            return make(Reference.class);
290        }
291    
292    }