1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.rop.cst;
18
19import com.android.dx.rop.type.Type;
20import java.util.HashMap;
21
22/**
23 * Constants that represent an arbitrary type (reference or primitive).
24 */
25public final class CstType extends TypedConstant {
26    /** {@code non-null;} map of interned types */
27    private static final HashMap<Type, CstType> interns =
28        new HashMap<Type, CstType>(100);
29
30    /** {@code non-null;} instance corresponding to the class {@code Object} */
31    public static final CstType OBJECT = intern(Type.OBJECT);
32
33    /** {@code non-null;} instance corresponding to the class {@code Boolean} */
34    public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS);
35
36    /** {@code non-null;} instance corresponding to the class {@code Byte} */
37    public static final CstType BYTE = intern(Type.BYTE_CLASS);
38
39    /** {@code non-null;} instance corresponding to the class {@code Character} */
40    public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS);
41
42    /** {@code non-null;} instance corresponding to the class {@code Double} */
43    public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS);
44
45    /** {@code non-null;} instance corresponding to the class {@code Float} */
46    public static final CstType FLOAT = intern(Type.FLOAT_CLASS);
47
48    /** {@code non-null;} instance corresponding to the class {@code Long} */
49    public static final CstType LONG = intern(Type.LONG_CLASS);
50
51    /** {@code non-null;} instance corresponding to the class {@code Integer} */
52    public static final CstType INTEGER = intern(Type.INTEGER_CLASS);
53
54    /** {@code non-null;} instance corresponding to the class {@code Short} */
55    public static final CstType SHORT = intern(Type.SHORT_CLASS);
56
57    /** {@code non-null;} instance corresponding to the class {@code Void} */
58    public static final CstType VOID = intern(Type.VOID_CLASS);
59
60    /** {@code non-null;} instance corresponding to the type {@code boolean[]} */
61    public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY);
62
63    /** {@code non-null;} instance corresponding to the type {@code byte[]} */
64    public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY);
65
66    /** {@code non-null;} instance corresponding to the type {@code char[]} */
67    public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY);
68
69    /** {@code non-null;} instance corresponding to the type {@code double[]} */
70    public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY);
71
72    /** {@code non-null;} instance corresponding to the type {@code float[]} */
73    public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY);
74
75    /** {@code non-null;} instance corresponding to the type {@code long[]} */
76    public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY);
77
78    /** {@code non-null;} instance corresponding to the type {@code int[]} */
79    public static final CstType INT_ARRAY = intern(Type.INT_ARRAY);
80
81    /** {@code non-null;} instance corresponding to the type {@code short[]} */
82    public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY);
83
84    /** {@code non-null;} the underlying type */
85    private final Type type;
86
87    /**
88     * {@code null-ok;} the type descriptor corresponding to this instance, if
89     * calculated
90     */
91    private CstString descriptor;
92
93    /**
94     * Returns an instance of this class that represents the wrapper
95     * class corresponding to a given primitive type. For example, if
96     * given {@link Type#INT}, this method returns the class reference
97     * {@code java.lang.Integer}.
98     *
99     * @param primitiveType {@code non-null;} the primitive type
100     * @return {@code non-null;} the corresponding wrapper class
101     */
102    public static CstType forBoxedPrimitiveType(Type primitiveType) {
103        switch (primitiveType.getBasicType()) {
104            case Type.BT_BOOLEAN: return BOOLEAN;
105            case Type.BT_BYTE:    return BYTE;
106            case Type.BT_CHAR:    return CHARACTER;
107            case Type.BT_DOUBLE:  return DOUBLE;
108            case Type.BT_FLOAT:   return FLOAT;
109            case Type.BT_INT:     return INTEGER;
110            case Type.BT_LONG:    return LONG;
111            case Type.BT_SHORT:   return SHORT;
112            case Type.BT_VOID:    return VOID;
113        }
114
115        throw new IllegalArgumentException("not primitive: " + primitiveType);
116    }
117
118    /**
119     * Returns an interned instance of this class for the given type.
120     *
121     * @param type {@code non-null;} the underlying type
122     * @return {@code non-null;} an appropriately-constructed instance
123     */
124    public static CstType intern(Type type) {
125        synchronized (interns) {
126            CstType cst = interns.get(type);
127
128            if (cst == null) {
129                cst = new CstType(type);
130                interns.put(type, cst);
131            }
132
133            return cst;
134        }
135    }
136
137    /**
138     * Constructs an instance.
139     *
140     * @param type {@code non-null;} the underlying type
141     */
142    public CstType(Type type) {
143        if (type == null) {
144            throw new NullPointerException("type == null");
145        }
146
147        if (type == type.KNOWN_NULL) {
148            throw new UnsupportedOperationException(
149                    "KNOWN_NULL is not representable");
150        }
151
152        this.type = type;
153        this.descriptor = null;
154    }
155
156    /** {@inheritDoc} */
157    @Override
158    public boolean equals(Object other) {
159        if (!(other instanceof CstType)) {
160            return false;
161        }
162
163        return type == ((CstType) other).type;
164    }
165
166    /** {@inheritDoc} */
167    @Override
168    public int hashCode() {
169        return type.hashCode();
170    }
171
172    /** {@inheritDoc} */
173    @Override
174    protected int compareTo0(Constant other) {
175        String thisDescriptor = type.getDescriptor();
176        String otherDescriptor = ((CstType) other).type.getDescriptor();
177        return thisDescriptor.compareTo(otherDescriptor);
178    }
179
180    /** {@inheritDoc} */
181    @Override
182    public String toString() {
183        return "type{" + toHuman() + '}';
184    }
185
186    /** {@inheritDoc} */
187    public Type getType() {
188        return Type.CLASS;
189    }
190
191    /** {@inheritDoc} */
192    @Override
193    public String typeName() {
194        return "type";
195    }
196
197    /** {@inheritDoc} */
198    @Override
199    public boolean isCategory2() {
200        return false;
201    }
202
203    /** {@inheritDoc} */
204    public String toHuman() {
205        return type.toHuman();
206    }
207
208    /**
209     * Gets the underlying type (as opposed to the type corresponding
210     * to this instance as a constant, which is always
211     * {@code Class}).
212     *
213     * @return {@code non-null;} the type corresponding to the name
214     */
215    public Type getClassType() {
216        return type;
217    }
218
219    /**
220     * Gets the type descriptor for this instance.
221     *
222     * @return {@code non-null;} the descriptor
223     */
224    public CstString getDescriptor() {
225        if (descriptor == null) {
226            descriptor = new CstString(type.getDescriptor());
227        }
228
229        return descriptor;
230    }
231
232    /**
233     * Returns a human readable package name for this type, like "java.util".
234     * If this is an array type, this returns the package name of the array's
235     * component type. If this is a primitive type, this returns "default".
236     */
237    public String getPackageName() {
238        // descriptor is a string like "[[Ljava/util/String;"
239        String descriptor = getDescriptor().getString();
240        int lastSlash = descriptor.lastIndexOf('/');
241        int lastLeftSquare = descriptor.lastIndexOf('['); // -1 unless this is an array
242        if (lastSlash == -1) {
243            return "default";
244        } else {
245            // +2 to skip the '[' and the 'L' prefix
246            return descriptor.substring(lastLeftSquare + 2, lastSlash).replace('/', '.');
247        }
248    }
249}
250