001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: Primitive.java 2 2011-02-05 21:51:43Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.java;
009    
010    import java.lang.reflect.InvocationTargetException;
011    import java.lang.reflect.Method;
012    import java.util.HashMap;
013    
014    /**
015     * Simple utility enumeration for working Java {@link Class} instances representing primitive types.
016     *
017     * <p>
018     * Instances of this class represent one of the eight Java primitive types.
019     * <p/>
020     */
021    public enum Primitive {
022    
023        BOOLEAN(Boolean.TYPE, Boolean.class, 'Z') {
024            public <R> R visit(PrimitiveSwitch<R> pswitch) {
025                return pswitch.caseBoolean();
026            }
027        },
028        BYTE(Byte.TYPE, Byte.class, 'B') {
029            public <R> R visit(PrimitiveSwitch<R> pswitch) {
030                return pswitch.caseByte();
031            }
032        },
033        CHARACTER(Character.TYPE, Character.class, 'C') {
034            public <R> R visit(PrimitiveSwitch<R> pswitch) {
035                return pswitch.caseCharacter();
036            }
037        },
038        SHORT(Short.TYPE, Short.class, 'S') {
039            public <R> R visit(PrimitiveSwitch<R> pswitch) {
040                return pswitch.caseShort();
041            }
042        },
043        INTEGER(Integer.TYPE, Integer.class, 'I') {
044            public <R> R visit(PrimitiveSwitch<R> pswitch) {
045                return pswitch.caseInteger();
046            }
047        },
048        FLOAT(Float.TYPE, Float.class, 'F') {
049            public <R> R visit(PrimitiveSwitch<R> pswitch) {
050                return pswitch.caseFloat();
051            }
052        },
053        LONG(Long.TYPE, Long.class, 'J') {
054            public <R> R visit(PrimitiveSwitch<R> pswitch) {
055                return pswitch.caseLong();
056            }
057        },
058        DOUBLE(Double.TYPE, Double.class, 'D') {
059            public <R> R visit(PrimitiveSwitch<R> pswitch) {
060                return pswitch.caseDouble();
061            }
062        };
063    
064        private static final HashMap<Class<?>, Primitive> CLASS_MAP = new HashMap<Class<?>, Primitive>();
065    
066        static {
067            for (Primitive prim : values()) {
068                CLASS_MAP.put(prim.primType, prim);
069                CLASS_MAP.put(prim.wrapType, prim);
070            }
071        }
072    
073        private final Class<?> primType;
074        private final Class<?> wrapType;
075        private final char letter;
076    
077        Primitive(Class<?> primType, Class<?> wrapType, char letter) {
078            this.primType = primType;
079            this.wrapType = wrapType;
080            this.letter = letter;
081        }
082    
083        public abstract <R> R visit(PrimitiveSwitch<R> pswitch);
084    
085        /**
086         * Get the short name for this primitive type, e.g., "int".
087         */
088        public String getName() {
089            return this.primType.getName();
090        }
091    
092        /**
093         * Get the long name for this primitive type, e.g., "Integer".
094         */
095        public String getLongName() {
096            return this.wrapType.getSimpleName();
097        }
098    
099        /**
100         * Get the single character descriptor for this primitive type, e.g., "I".
101         */
102        public char getLetter() {
103            return this.letter;
104        }
105    
106        /**
107         * Get the {@link Class} object representing this primitive type, e.g., {@code Integer.TYPE}.
108         */
109        public Class<?> getType() {
110            return this.primType;
111        }
112    
113        /**
114         * Get the wrapper {@link Class} object for this primitive type, e.g., {@code Integer.class}.
115         */
116        public Class<?> getWrapperType() {
117            return this.wrapType;
118        }
119    
120        /**
121         * Get the wrapper class' "unwrap" method for this primitive type, e.g., {@code Integer.intValue()}.
122         */
123        public Method getUnwrapMethod() {
124            try {
125                return this.wrapType.getMethod(getName() + "Value");
126            } catch (NoSuchMethodException e) {
127                throw new RuntimeException(e);
128            }
129        }
130    
131        /**
132         * Parse a string value using the type's {@code valueOf} method.
133         *
134         * @param string string representation of a value
135         * @throws IllegalArgumentException if {@code string} cannot be parsed
136         * @throws IllegalArgumentException if {@code string} is null
137         */
138        public Object parseValue(String string) {
139    
140            // Handle null
141            if (string == null)
142                throw new IllegalArgumentException("null string");
143    
144            // Special case: Character doesn't have a valueOf(String) method
145            if (this == CHARACTER) {
146                if (string.length() != 1)
147                    throw new IllegalArgumentException("not a character: \"" + string + "\"");
148                return Character.valueOf(string.charAt(0));
149            }
150    
151            // Find this wrapper class' valueOf() method
152            Method method;
153            try {
154                method = this.wrapType.getMethod("valueOf", String.class);
155            } catch (NoSuchMethodException e) {
156                throw new RuntimeException("unexpected exception", e);
157            }
158    
159            // Use it to parse the value
160            try {
161                return method.invoke(null, string);
162            } catch (IllegalAccessException e) {
163                throw new RuntimeException("unexpected exception", e);
164            } catch (IllegalArgumentException e) {
165                throw new RuntimeException("unexpected exception", e);
166            } catch (InvocationTargetException e) {
167                Throwable nested = e.getTargetException();
168                if (nested instanceof IllegalArgumentException)
169                    throw (IllegalArgumentException)nested;
170                throw new RuntimeException("unexpected exception", nested);
171            }
172        }
173    
174        /**
175         * Get the value corresponding to the given Java primitive or primitive wrapper type.
176         *
177         * @return the {@link Primitive} corresponding to {@code c}, or {@code null}
178         *         if {@code c} is not a primitive or primitive wrapper type
179         */
180        public static Primitive get(Class<?> c) {
181            return CLASS_MAP.get(c);
182        }
183    }
184