| 1 | /* |
| 2 | * This file is part of JON. |
| 3 | * |
| 4 | * JON is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU Lesser General Public License as published by |
| 6 | * the Free Software Foundation; either version 3 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * JON is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU Lesser General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Lesser General Public License |
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | * |
| 17 | * Copyright 2007 Michael Doberenz |
| 18 | */ |
| 19 | package org.fuwjax.jon.type; |
| 20 | |
| 21 | import java.lang.reflect.Method; |
| 22 | import java.util.HashMap; |
| 23 | import java.util.Map; |
| 24 | |
| 25 | import org.fuwjax.jon.ClassCastStrategy; |
| 26 | import org.fuwjax.jon.IndirectType; |
| 27 | import org.fuwjax.jon.ObjectAccessException; |
| 28 | import org.fuwjax.jon.ReferenceStrategy; |
| 29 | import org.fuwjax.jon.accessor.Literal; |
| 30 | import org.fuwjax.jon.accessor.LiteralAccessor; |
| 31 | import org.fuwjax.jon.accessor.Symbol; |
| 32 | |
| 33 | /** |
| 34 | * Allows abstracted access to a primitive data type. |
| 35 | * @author michaeldoberenz |
| 36 | */ |
| 37 | public class PrimitiveType extends AbstractIndirectType implements LiteralAccessor{ |
| 38 | private static final String DOUBLE_SUFFIX = "D"; //$NON-NLS-1$ |
| 39 | private static final String FLOAT_SUFFIX = "F"; //$NON-NLS-1$ |
| 40 | private static final String LONG_SUFFIX = "L"; //$NON-NLS-1$ |
| 41 | private static final String TYPE = "TYPE"; //$NON-NLS-1$ |
| 42 | private static final String VALUE_OF = "valueOf"; //$NON-NLS-1$ |
| 43 | private static final Map<Class<?>, String> SUFFIX; |
| 44 | private static final Map<Class<?>, Class<?>> WRAPPERS; |
| 45 | static{ |
| 46 | WRAPPERS = new HashMap<Class<?>, Class<?>>(); |
| 47 | WRAPPERS.put(int.class, Integer.class); |
| 48 | WRAPPERS.put(short.class, Short.class); |
| 49 | WRAPPERS.put(boolean.class, Boolean.class); |
| 50 | WRAPPERS.put(byte.class, Byte.class); |
| 51 | WRAPPERS.put(long.class, Long.class); |
| 52 | WRAPPERS.put(double.class, Double.class); |
| 53 | WRAPPERS.put(float.class, Float.class); |
| 54 | WRAPPERS.put(char.class, Character.class); |
| 55 | SUFFIX = new HashMap<Class<?>, String>(); |
| 56 | SUFFIX.put(long.class, LONG_SUFFIX); |
| 57 | SUFFIX.put(Long.class, LONG_SUFFIX); |
| 58 | SUFFIX.put(float.class, FLOAT_SUFFIX); |
| 59 | SUFFIX.put(Float.class, FLOAT_SUFFIX); |
| 60 | SUFFIX.put(Double.class, DOUBLE_SUFFIX); |
| 61 | SUFFIX.put(Double.class, DOUBLE_SUFFIX); |
| 62 | } |
| 63 | private boolean castNext; |
| 64 | private String binaryCast; |
| 65 | |
| 66 | /** |
| 67 | * Creates a new instance for <code>cls</code>. |
| 68 | * @param cls the primitive or primitive wrapper backing this abstract type. |
| 69 | */ |
| 70 | public PrimitiveType(final Class<?> cls){ |
| 71 | super(cls); |
| 72 | binaryCast = SUFFIX.get(cls); |
| 73 | if(binaryCast == null){ |
| 74 | binaryCast = ""; //$NON-NLS-1$ |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Returns true if <code>cls</code> is a primitive or primitive wrapper, |
| 80 | * false otherwise. |
| 81 | * @param cls the class under test |
| 82 | * @return true if <code>cls</code> is a primitive or primitive wrapper, |
| 83 | * false otherwise |
| 84 | */ |
| 85 | public static boolean isPrimitive(final Class<?> cls){ |
| 86 | return cls.isPrimitive() || isPrimitiveWrapper(cls); |
| 87 | } |
| 88 | |
| 89 | /** |
| 90 | * Returns true if <code>cls</code> is a primitive wrapper, false |
| 91 | * otherwise. |
| 92 | * @param cls the class under test |
| 93 | * @return true if <code>cls</code> is a primitive wrapper, false otherwise |
| 94 | */ |
| 95 | public static boolean isPrimitiveWrapper(final Class<?> cls){ |
| 96 | try{ |
| 97 | if(!(isPrimitiveWrapperChild(cls))){ |
| 98 | return false; |
| 99 | } |
| 100 | return getPrimitiveClassType(cls).isPrimitive(); |
| 101 | }catch(Exception e){ |
| 102 | return false; |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | private static boolean isPrimitiveWrapperChild(final Class<?> cls){ |
| 107 | return Number.class.isAssignableFrom(cls) || Boolean.class.isAssignableFrom(cls); |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Returns the primitive type for <code>cls</code> if it is a primitive |
| 112 | * wrapper, throws an exception otherwise. |
| 113 | * @param cls the class under test |
| 114 | * @return the primitive type if <code>cls</code> is a wrapper |
| 115 | * @throws Exception if cls is not a primitive wrapper |
| 116 | */ |
| 117 | public static Class<?> getPrimitiveClassType(final Class<?> cls) throws Exception{ |
| 118 | return (Class<?>)cls.getDeclaredField(TYPE).get(null); |
| 119 | } |
| 120 | |
| 121 | public ClassCastStrategy getClassCastStrategy(){ |
| 122 | return ClassCastStrategy.PrimitiveCast; |
| 123 | } |
| 124 | |
| 125 | public ReferenceStrategy getReferenceStrategy(){ |
| 126 | return ReferenceStrategy.NeverReference; |
| 127 | } |
| 128 | |
| 129 | public Symbol getSymbolSpec(){ |
| 130 | return Symbol.Literal; |
| 131 | } |
| 132 | |
| 133 | public Literal getLiteral(){ |
| 134 | return Literal.Identifier; |
| 135 | } |
| 136 | |
| 137 | public CharSequence toString(final Object object) throws ObjectAccessException{ |
| 138 | if(castNext){ |
| 139 | castNext = false; |
| 140 | return object.toString() + binaryCast; |
| 141 | } |
| 142 | return object.toString(); |
| 143 | } |
| 144 | |
| 145 | private Class<?> getWrapper(){ |
| 146 | final Class<?> wrapper = WRAPPERS.get(getType()); |
| 147 | if(wrapper == null){ |
| 148 | return getType(); |
| 149 | } |
| 150 | return wrapper; |
| 151 | } |
| 152 | |
| 153 | public Object createInstance(final CharSequence parse, final Object source) throws ObjectAccessException{ |
| 154 | try{ |
| 155 | final Method valueOf = getWrapper().getDeclaredMethod(VALUE_OF, String.class); |
| 156 | return valueOf.invoke(null, parse.toString()); |
| 157 | }catch(IllegalAccessException e){ |
| 158 | throw ObjectAccessException.Message.MethodInaccessible.exception(e); |
| 159 | }catch(Exception e){ |
| 160 | throw ObjectAccessException.Message.MethodFailure.exception(e); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Called if the next toString call should include a cast. |
| 166 | * @param expectedType the originally expected type |
| 167 | */ |
| 168 | public void castNext(final IndirectType expectedType){ |
| 169 | if(expectedType instanceof PrimitiveType){ |
| 170 | if(getWrapper().equals(((PrimitiveType)expectedType).getWrapper())){ |
| 171 | return; |
| 172 | } |
| 173 | } |
| 174 | castNext = true; |
| 175 | } |
| 176 | } |