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