1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.meta;
20
21 import java.io.InputStream;
22 import java.io.Reader;
23 import java.io.Serializable;
24 import java.lang.reflect.Array;
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Calendar;
30 import java.util.Collection;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Properties;
38
39 import org.apache.openjpa.enhance.PersistenceCapable;
40 import org.apache.openjpa.lib.meta.CFMetaDataParser;
41 import org.apache.openjpa.lib.util.Localizer;
42 import org.apache.openjpa.util.MetaDataException;
43 import serp.util.Numbers;
44 import serp.util.Strings;
45
46 /**
47 * Type constants for managed fields.
48 *
49 * @author Abe White
50 */
51 public class JavaTypes {
52
53 public static final int BOOLEAN = 0;
54 public static final int BYTE = 1;
55 public static final int CHAR = 2;
56 public static final int DOUBLE = 3;
57 public static final int FLOAT = 4;
58 public static final int INT = 5;
59 public static final int LONG = 6;
60 public static final int SHORT = 7;
61 // keep OBJECT as first non-primitive type code; other code relies on it
62 public static final int OBJECT = 8;
63 public static final int STRING = 9;
64 public static final int NUMBER = 10;
65 public static final int ARRAY = 11;
66 public static final int COLLECTION = 12;
67 public static final int MAP = 13;
68 public static final int DATE = 14;
69 public static final int PC = 15;
70 public static final int BOOLEAN_OBJ = 16;
71 public static final int BYTE_OBJ = 17;
72 public static final int CHAR_OBJ = 18;
73 public static final int DOUBLE_OBJ = 19;
74 public static final int FLOAT_OBJ = 20;
75 public static final int INT_OBJ = 21;
76 public static final int LONG_OBJ = 22;
77 public static final int SHORT_OBJ = 23;
78 public static final int BIGDECIMAL = 24;
79 public static final int BIGINTEGER = 25;
80 public static final int LOCALE = 26;
81 public static final int PC_UNTYPED = 27;
82 public static final int CALENDAR = 28;
83 public static final int OID = 29;
84 public static final int INPUT_STREAM = 30;
85 public static final int INPUT_READER = 31;
86
87 private static final Localizer _loc = Localizer.forPackage(JavaTypes.class);
88
89 private static final Map _typeCodes = new HashMap();
90
91 static {
92 _typeCodes.put(String.class, Numbers.valueOf(STRING));
93 _typeCodes.put(Boolean.class, Numbers.valueOf(BOOLEAN_OBJ));
94 _typeCodes.put(Byte.class, Numbers.valueOf(BYTE_OBJ));
95 _typeCodes.put(Character.class, Numbers.valueOf(CHAR_OBJ));
96 _typeCodes.put(Double.class, Numbers.valueOf(DOUBLE_OBJ));
97 _typeCodes.put(Float.class, Numbers.valueOf(FLOAT_OBJ));
98 _typeCodes.put(Integer.class, Numbers.valueOf(INT_OBJ));
99 _typeCodes.put(Long.class, Numbers.valueOf(LONG_OBJ));
100 _typeCodes.put(Short.class, Numbers.valueOf(SHORT_OBJ));
101 _typeCodes.put(Date.class, Numbers.valueOf(DATE));
102 _typeCodes.put(java.sql.Date.class, Numbers.valueOf(DATE));
103 _typeCodes.put(java.sql.Timestamp.class, Numbers.valueOf(DATE));
104 _typeCodes.put(java.sql.Time.class, Numbers.valueOf(DATE));
105 _typeCodes.put(BigInteger.class, Numbers.valueOf(BIGINTEGER));
106 _typeCodes.put(BigDecimal.class, Numbers.valueOf(BIGDECIMAL));
107 _typeCodes.put(Number.class, Numbers.valueOf(NUMBER));
108 _typeCodes.put(Locale.class, Numbers.valueOf(LOCALE));
109 _typeCodes.put(Object.class, Numbers.valueOf(OBJECT));
110 _typeCodes.put(PersistenceCapable.class, Numbers.valueOf(PC_UNTYPED));
111 _typeCodes.put(Properties.class, Numbers.valueOf(MAP));
112 _typeCodes.put(Calendar.class, Numbers.valueOf(CALENDAR));
113 }
114
115 /**
116 * Return the field metadata type code for the given class. First class
117 * objects are not recognized in this method.
118 */
119 public static int getTypeCode(Class type) {
120 if (type == null)
121 return OBJECT;
122
123 if (type.isPrimitive()) {
124 switch (type.getName().charAt(0)) {
125 case 'b':
126 return (type == boolean.class) ? BOOLEAN : BYTE;
127 case 'c':
128 return CHAR;
129 case 'd':
130 return DOUBLE;
131 case 'f':
132 return FLOAT;
133 case 'i':
134 return INT;
135 case 'l':
136 return LONG;
137 case 's':
138 return SHORT;
139 }
140 }
141
142 Integer code = (Integer) _typeCodes.get(type);
143 if (code != null)
144 return code.intValue();
145
146 // have to do this first to catch custom collection and map types;
147 // on resolve we figure out if these custom types are
148 // persistence-capable
149 if (Collection.class.isAssignableFrom(type))
150 return COLLECTION;
151 if (Map.class.isAssignableFrom(type))
152 return MAP;
153 if (type.isArray())
154 return ARRAY;
155 if (Calendar.class.isAssignableFrom(type))
156 return CALENDAR;
157
158 if (type.isInterface()) {
159 if (type == Serializable.class)
160 return OBJECT;
161 return PC_UNTYPED;
162 }
163 if (type.isAssignableFrom(Reader.class))
164 return INPUT_READER;
165 if (type.isAssignableFrom (InputStream.class))
166 return INPUT_STREAM;
167
168 return OBJECT;
169 }
170
171 /**
172 * Check the given name against the same set of standard packages used
173 * when parsing metadata.
174 */
175 public static Class classForName(String name, ClassMetaData context) {
176 return classForName(name, context, null);
177 }
178
179 /**
180 * Check the given name against the same set of standard packages used
181 * when parsing metadata.
182 */
183 public static Class classForName(String name, ClassMetaData context,
184 ClassLoader loader) {
185 return classForName(name, context, context.getDescribedType(), null,
186 loader);
187 }
188
189 /**
190 * Check the given name against the same set of standard packages used
191 * when parsing metadata.
192 */
193 public static Class classForName(String name, ValueMetaData context) {
194 return classForName(name, context, null);
195 }
196
197 /**
198 * Check the given name against the same set of standard packages used
199 * when parsing metadata.
200 */
201 public static Class classForName(String name, ValueMetaData context,
202 ClassLoader loader) {
203 return classForName(name,
204 context.getFieldMetaData().getDefiningMetaData(),
205 context.getFieldMetaData().getDeclaringType(), context, loader);
206 }
207
208 /**
209 * Check the given name against the same set of standard packages used
210 * when parsing metadata.
211 */
212 private static Class classForName(String name, ClassMetaData meta,
213 Class dec, ValueMetaData vmd, ClassLoader loader) {
214 // special case for PersistenceCapable and Object
215 if ("PersistenceCapable".equals(name)
216 || "javax.jdo.PersistenceCapable".equals(name)) // backwards compat
217 return PersistenceCapable.class;
218 if ("Object".equals(name))
219 return Object.class;
220
221 MetaDataRepository rep = meta.getRepository();
222 boolean runtime = (rep.getValidate() & rep.VALIDATE_RUNTIME) != 0;
223 if (loader == null)
224 loader = rep.getConfiguration().getClassResolverInstance().
225 getClassLoader(dec, meta.getEnvClassLoader());
226
227 // try the owner's package
228 String pkg = Strings.getPackageName(dec);
229 Class cls = CFMetaDataParser.classForName(name, pkg, runtime, loader);
230 if (cls == null && vmd != null) {
231 // try against this value type's package too
232 pkg = Strings.getPackageName(vmd.getDeclaredType());
233 cls = CFMetaDataParser.classForName(name, pkg, runtime, loader);
234 }
235 if (cls == null)
236 throw new MetaDataException(_loc.get("bad-class", name,
237 (vmd == null) ? (Object) meta : (Object) vmd));
238 return cls;
239 }
240
241 /**
242 * Convert the given object to the given type if possible. If the type is
243 * a numeric primitive, this method only guarantees that the return value
244 * is a {@link Number}. If no known conversion or the value is null,
245 * returns the original value.
246 */
247 public static Object convert(Object val, int typeCode) {
248 if (val == null)
249 return null;
250
251 switch (typeCode) {
252 case BIGDECIMAL:
253 if (val instanceof BigDecimal)
254 return val;
255 if (val instanceof Number)
256 return new BigDecimal(((Number) val).doubleValue());
257 if (val instanceof String)
258 return new BigDecimal(val.toString());
259 return val;
260 case BIGINTEGER:
261 if (val instanceof BigInteger)
262 return val;
263 if (val instanceof Number || val instanceof String)
264 return new BigInteger(val.toString());
265 return val;
266 case BOOLEAN:
267 case BOOLEAN_OBJ:
268 if (val instanceof String)
269 return Boolean.valueOf(val.toString());
270 return val;
271 case BYTE_OBJ:
272 if (val instanceof Byte)
273 return val;
274 if (val instanceof Number)
275 return new Byte(((Number) val).byteValue());
276 // no break
277 case BYTE:
278 if (val instanceof String)
279 return new Byte(val.toString());
280 return val;
281 case CHAR:
282 case CHAR_OBJ:
283 if (val instanceof Character)
284 return val;
285 if (val instanceof String)
286 return new Character(val.toString().charAt(0));
287 if (val instanceof Number)
288 return new Character((char) ((Number) val).intValue());
289 return val;
290 case DATE:
291 if (val instanceof String)
292 return new Date(val.toString());
293 return val;
294 case DOUBLE_OBJ:
295 if (val instanceof Double)
296 return val;
297 if (val instanceof Number)
298 return new Double(((Number) val).doubleValue());
299 // no break
300 case DOUBLE:
301 if (val instanceof String)
302 return new Double(val.toString());
303 return val;
304 case FLOAT_OBJ:
305 if (val instanceof Float)
306 return val;
307 if (val instanceof Number)
308 return new Float(((Number) val).floatValue());
309 // no break
310 case FLOAT:
311 if (val instanceof String)
312 return new Float(val.toString());
313 return val;
314 case INT_OBJ:
315 if (val instanceof Integer)
316 return val;
317 if (val instanceof Number)
318 return Numbers.valueOf(((Number) val).intValue());
319 // no break
320 case INT:
321 if (val instanceof String)
322 return new Integer(val.toString());
323 return val;
324 case LONG_OBJ:
325 if (val instanceof Long)
326 return val;
327 if (val instanceof Number)
328 return Numbers.valueOf(((Number) val).longValue());
329 // no break
330 case LONG:
331 if (val instanceof String)
332 return new Long(val.toString());
333 return val;
334 case NUMBER:
335 if (val instanceof Number)
336 return val;
337 if (val instanceof String)
338 return new BigDecimal(val.toString());
339 return val;
340 case SHORT_OBJ:
341 if (val instanceof Short)
342 return val;
343 if (val instanceof Number)
344 return new Short(((Number) val).shortValue());
345 // no break
346 case SHORT:
347 if (val instanceof String)
348 return new Short(val.toString());
349 return val;
350 case STRING:
351 return val.toString();
352 default:
353 return val;
354 }
355 }
356
357 /**
358 * Return true if the (possibly unresolved) field or its elements might be
359 * persistence capable objects.
360 */
361 public static boolean maybePC(FieldMetaData field) {
362 switch (field.getDeclaredTypeCode()) {
363 case JavaTypes.ARRAY:
364 case JavaTypes.COLLECTION:
365 return maybePC(field.getElement());
366 case JavaTypes.MAP:
367 return maybePC(field.getKey()) || maybePC(field.getElement());
368 default:
369 return maybePC((ValueMetaData) field);
370 }
371 }
372
373 /**
374 * Return true if the (possibly unresolved) value might be a first class
375 * object.
376 */
377 public static boolean maybePC(ValueMetaData val) {
378 return maybePC(val.getDeclaredTypeCode(), val.getDeclaredType());
379 }
380
381 /**
382 * Return true if the given unresolved typecode/type pair may represent a
383 * persistent object.
384 */
385 static boolean maybePC(int typeCode, Class type) {
386 if (type == null)
387 return false;
388 switch (typeCode) {
389 case JavaTypes.OBJECT:
390 case JavaTypes.PC:
391 case JavaTypes.PC_UNTYPED:
392 return true;
393 case JavaTypes.COLLECTION:
394 case JavaTypes.MAP:
395 return !type.getName().startsWith("java.util.");
396 default:
397 return false;
398 }
399 }
400
401 /**
402 * Helper method to return the given array value as a collection.
403 */
404 public static List toList(Object val, Class elem, boolean mutable) {
405 if (val == null)
406 return null;
407
408 List l;
409 if (!elem.isPrimitive()) {
410 // if an object array, use built-in list function
411 l = Arrays.asList((Object[]) val);
412 if (mutable)
413 l = new ArrayList(l);
414 } else {
415 // convert to list of wrapper objects
416 int length = Array.getLength(val);
417 l = new ArrayList(length);
418 for (int i = 0; i < length; i++)
419 l.add(Array.get(val, i));
420 }
421 return l;
422 }
423
424 /**
425 * Helper method to return the given collection as an array.
426 */
427 public static Object toArray(Collection coll, Class elem) {
428 if (coll == null)
429 return null;
430
431 Object array = Array.newInstance(elem, coll.size());
432 int idx = 0;
433 for (Iterator itr = coll.iterator(); itr.hasNext(); idx++)
434 Array.set(array, idx, itr.next ());
435 return array;
436 }
437 }