1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package javax.jdo.spi;
23
24 import java.lang.reflect.Constructor;
25
26 import java.text.DateFormat;
27 import java.text.ParsePosition;
28 import java.text.SimpleDateFormat;
29
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Currency;
34 import java.util.Date;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.WeakHashMap;
42
43 import javax.jdo.JDOException;
44 import javax.jdo.JDOFatalInternalException;
45 import javax.jdo.JDOFatalUserException;
46 import javax.jdo.JDOUserException;
47 import javax.jdo.spi.JDOPermission;
48
49 /*** This class is a helper class for JDO implementations. It contains methods
50 * to register metadata for persistence-capable classes and to perform common
51 * operations needed by implementations, not by end users.
52 * <P><code>JDOImplHelper</code> allows construction of instances of persistence-capable
53 * classes without using reflection.
54 * <P>Persistence-capable classes register themselves via a static method
55 * at class load time.
56 * There is no security restriction on this access. JDO implementations
57 * get access to the functions provided by this class only if they are
58 * authorized by the security manager. To avoid having every call go through
59 * the security manager, only the call to get an instance is checked. Once an
60 * implementation
61 * has an instance, any of the methods can be invoked without security checks.
62 * @version 1.0.2
63 *
64 */
65 public class JDOImplHelper extends java.lang.Object {
66
67 /*** This synchronized <code>HashMap</code> contains a static mapping of
68 * <code>PersistenceCapable</code> class to
69 * metadata for the class used for constructing new instances. New entries
70 * are added by the static method in each <code>PersistenceCapable</code> class.
71 * Entries are never removed.
72 */
73 private static Map registeredClasses = Collections.synchronizedMap(new HashMap ());
74
75 /*** This Set contains all classes that have registered for setStateManager
76 * permissions via authorizeStateManagerClass.
77 */
78 private static Map authorizedStateManagerClasses = new WeakHashMap();
79
80 /*** This list contains the registered listeners for <code>RegisterClassEvent</code>s.
81 */
82 private static List listeners = new ArrayList();
83
84 /*** The list of registered StateInterrogation instances
85 */
86 private static List stateInterrogations = new ArrayList();
87
88 /*** The singleton <code>JDOImplHelper</code> instance.
89 */
90 private static JDOImplHelper jdoImplHelper = new JDOImplHelper();
91
92 /*** The Internationalization message helper.
93 */
94 private final static I18NHelper msg = I18NHelper.getInstance ("javax.jdo.Bundle");
95
96 /*** The DateFormat pattern.
97 */
98 private static String dateFormatPattern;
99
100 /*** The default DateFormat instance.
101 */
102 private static DateFormat dateFormat;
103
104 /*** Register the default DateFormat instance.
105 */
106 static {
107 jdoImplHelper.registerDateFormat(DateFormat.getDateTimeInstance());
108 }
109
110 /*** Creates new JDOImplHelper */
111 private JDOImplHelper() {
112 }
113
114 /*** Get an instance of <code>JDOImplHelper</code>. This method
115 * checks that the caller is authorized for <code>JDOPermission("getMetadata")</code>,
116 * and if not, throws <code>SecurityException</code>.
117 * @return an instance of <code>JDOImplHelper</code>.
118 * @throws SecurityException if the caller is not authorized for JDOPermission("getMetadata").
119 */
120 public static JDOImplHelper getInstance()
121 throws SecurityException {
122 SecurityManager sec = System.getSecurityManager();
123 if (sec != null) {
124
125 sec.checkPermission (JDOPermission.GET_METADATA);
126 }
127 return jdoImplHelper;
128 }
129
130 /*** Get the field names for a <code>PersistenceCapable</code> class. The order
131 * of fields is the natural ordering of the <code>String</code> class (without
132 * considering localization).
133 * @param pcClass the <code>PersistenceCapable</code> class.
134 * @return the field names for the class.
135 */
136 public String[] getFieldNames (Class pcClass) {
137 Meta meta = getMeta (pcClass);
138 return meta.getFieldNames();
139 }
140
141 /*** Get the field types for a <code>PersistenceCapable</code> class. The order
142 * of fields is the same as for field names.
143 * @param pcClass the <code>PersistenceCapable</code> class.
144 * @return the field types for the class.
145 */
146 public Class[] getFieldTypes (Class pcClass) {
147 Meta meta = getMeta (pcClass);
148 return meta.getFieldTypes();
149 }
150
151 /*** Get the field flags for a <code>PersistenceCapable</code> class. The order
152 * of fields is the same as for field names.
153 * @param pcClass the <code>PersistenceCapable</code> class.
154 * @return the field types for the class.
155 */
156 public byte[] getFieldFlags (Class pcClass) {
157 Meta meta = getMeta (pcClass);
158 return meta.getFieldFlags();
159 }
160
161 /*** Get the persistence-capable superclass for a <code>PersistenceCapable</code> class.
162 * @param pcClass the <code>PersistenceCapable</code> class.
163 * @return The <code>PersistenceCapable</code> superclass for this class,
164 * or <code>null</code> if there isn't one.
165 */
166 public Class getPersistenceCapableSuperclass (Class pcClass) {
167 Meta meta = getMeta (pcClass);
168 return meta.getPersistenceCapableSuperclass();
169 }
170
171
172 /*** Create a new instance of the class and assign its <code>jdoStateManager</code>.
173 * The new instance has its <code>jdoFlags</code> set to <code>LOAD_REQUIRED</code>.
174 * @see PersistenceCapable#jdoNewInstance(StateManager sm)
175 * @param pcClass the <code>PersistenceCapable</code> class.
176 * @param sm the <code>StateManager</code> which will own the new instance.
177 * @return the new instance, or <code>null</code> if the class is not registered.
178 */
179 public PersistenceCapable newInstance (Class pcClass, StateManager sm) {
180 Meta meta = getMeta (pcClass);
181 PersistenceCapable pcInstance = meta.getPC();
182 return pcInstance == null?null:pcInstance.jdoNewInstance(sm);
183 }
184
185 /*** Create a new instance of the class and assign its <code>jdoStateManager</code> and
186 * key values from the ObjectId. If the oid parameter is <code>null</code>,
187 * no key values are copied.
188 * The new instance has its <code>jdoFlags</code> set to <code>LOAD_REQUIRED</code>.
189 * @see PersistenceCapable#jdoNewInstance(StateManager sm, Object oid)
190 * @param pcClass the <code>PersistenceCapable</code> class.
191 * @param sm the <code>StateManager</code> which will own the new instance.
192 * @return the new instance, or <code>null</code> if the class is not registered.
193 * @param oid the ObjectId instance from which to copy key field values.
194 */
195 public PersistenceCapable newInstance
196 (Class pcClass, StateManager sm, Object oid) {
197 Meta meta = getMeta (pcClass);
198 PersistenceCapable pcInstance = meta.getPC();
199 return pcInstance == null?null:pcInstance.jdoNewInstance(sm, oid);
200 }
201
202 /*** Create a new instance of the ObjectId class of this
203 * <code>PersistenceCapable</code> class.
204 * It is intended only for application identity. This method should
205 * not be called for classes that use single field identity;
206 * newObjectIdInstance(Class, Object) should be used instead.
207 * If the class has been
208 * enhanced for datastore identity, or if the class is abstract,
209 * null is returned.
210 * @param pcClass the <code>PersistenceCapable</code> class.
211 * @return the new ObjectId instance, or <code>null</code> if the class
212 * is not registered.
213 */
214 public Object newObjectIdInstance (Class pcClass) {
215 Meta meta = getMeta (pcClass);
216 PersistenceCapable pcInstance = meta.getPC();
217 return pcInstance == null?null:pcInstance.jdoNewObjectIdInstance();
218 }
219
220 /*** Create a new instance of the class used by the parameter Class
221 * for JDO identity, using the
222 * key constructor of the object id class. It is intended for single
223 * field identity. The identity
224 * instance returned has no relationship with the values of the primary key
225 * fields of the persistence-capable instance on which the method is called.
226 * If the key is the wrong class for the object id class, null is returned.
227 * <P>For classes that use single field identity, if the parameter is
228 * of one of the following types, the behavior must be as specified:
229 * <ul><li><code>Number</code> or <code>Character</code>: the
230 * parameter must be the single field
231 * type or the wrapper class of the primitive field type; the parameter
232 * is passed to the single field identity constructor
233 * </li><li><code>ObjectIdFieldSupplier</code>: the field value
234 * is fetched from the <code>ObjectIdFieldSupplier</code> and passed to the
235 * single field identity constructor
236 * </li><li><code>String</code>: the String is passed to the
237 * single field identity constructor
238 * </li></ul>
239 * @return the new ObjectId instance, or <code>null</code>
240 * if the class is not registered.
241 * @param obj the <code>Object</code> form of the object id
242 * @param pcClass the <code>PersistenceCapable</code> class.
243 * @since 2.0
244 */
245 public Object newObjectIdInstance (Class pcClass, Object obj) {
246 Meta meta = getMeta (pcClass);
247 PersistenceCapable pcInstance = meta.getPC();
248 return (pcInstance == null)?null:pcInstance.jdoNewObjectIdInstance(obj);
249 }
250
251 /*** Copy fields from an outside source to the key fields in the ObjectId.
252 * This method is generated in the <code>PersistenceCapable</code> class to
253 * generate a call to the field manager for each key field in the ObjectId.
254 * <P>For example, an ObjectId class that has three key fields (<code>int id</code>,
255 * <code>String name</code>, and <code>Float salary</code>) would have the method generated:
256 * <P><code>
257 * void jdoCopyKeyFieldsToObjectId (Object oid, ObjectIdFieldSupplier fm) {
258 * <BR> oid.id = fm.fetchIntField (0);
259 * <BR> oid.name = fm.fetchStringField (1);
260 * <BR> oid.salary = fm.fetchObjectField (2);
261 * <BR>}</code>
262 * <P>The implementation is responsible for implementing the
263 * <code>ObjectIdFieldSupplier</code> to provide the values for the key fields.
264 * @param pcClass the <code>PersistenceCapable Class</code>.
265 * @param oid the ObjectId target of the copy.
266 * @param fm the field manager that supplies the field values.
267 */
268 public void copyKeyFieldsToObjectId
269 (Class pcClass, PersistenceCapable.ObjectIdFieldSupplier fm, Object oid) {
270 Meta meta = getMeta (pcClass);
271 PersistenceCapable pcInstance = meta.getPC();
272 if (pcInstance == null) {
273 throw new JDOFatalInternalException (
274 msg.msg("ERR_AbstractClassNoIdentity", pcClass.getName()));
275 }
276 pcInstance.jdoCopyKeyFieldsToObjectId(fm, oid);
277 }
278
279 /*** Copy fields to an outside source from the key fields in the ObjectId.
280 * This method is generated in the <code>PersistenceCapable</code> class to generate
281 * a call to the field manager for each key field in the ObjectId. For
282 * example, an ObjectId class that has three key fields (<code>int id</code>,
283 * <code>String name</code>, and <code>Float salary</code>) would have the method generated:
284 * <P><code>void jdoCopyKeyFieldsFromObjectId
285 * <BR> (PersistenceCapable oid, ObjectIdFieldConsumer fm) {
286 * <BR> fm.storeIntField (0, oid.id);
287 * <BR> fm.storeStringField (1, oid.name);
288 * <BR> fm.storeObjectField (2, oid.salary);
289 * <BR>}</code>
290 * <P>The implementation is responsible for implementing the
291 * <code>ObjectIdFieldConsumer</code> to store the values for the key fields.
292 * @param pcClass the <code>PersistenceCapable</code> class
293 * @param oid the ObjectId source of the copy.
294 * @param fm the field manager that receives the field values.
295 */
296 public void copyKeyFieldsFromObjectId
297 (Class pcClass, PersistenceCapable.ObjectIdFieldConsumer fm, Object oid) {
298 Meta meta = getMeta (pcClass);
299 PersistenceCapable pcInstance = meta.getPC();
300 if (pcInstance == null) {
301 throw new JDOFatalInternalException (
302 msg.msg("ERR_AbstractClassNoIdentity", pcClass.getName()));
303 }
304 pcInstance.jdoCopyKeyFieldsFromObjectId(fm, oid);
305 }
306
307 /*** Register metadata by class. The registration will be done in the
308 * class named <code>JDOImplHelper</code> loaded by the same or an
309 * ancestor class loader as the <code>PersistenceCapable</code> class
310 * performing the registration.
311 *
312 * @param pcClass the <code>PersistenceCapable</code> class
313 * used as the key for lookup.
314 * @param fieldNames an array of <code>String</code> field names for persistent and transactional fields
315 * @param fieldTypes an array of <code>Class</code> field types
316 * @param fieldFlags the Field Flags for persistent and transactional fields
317 * @param pc an instance of the <code>PersistenceCapable</code> class
318 * @param persistenceCapableSuperclass the most immediate superclass that is <code>PersistenceCapable</code>
319 */
320 public static void registerClass (Class pcClass,
321 String[] fieldNames, Class[] fieldTypes,
322 byte[] fieldFlags, Class persistenceCapableSuperclass,
323 PersistenceCapable pc) {
324 if (pcClass == null)
325 throw new NullPointerException(msg.msg("ERR_NullClass"));
326 Meta meta = new Meta (fieldNames, fieldTypes,
327 fieldFlags, persistenceCapableSuperclass, pc);
328 registeredClasses.put (pcClass, meta);
329
330
331 synchronized (listeners) {
332 if (!listeners.isEmpty()) {
333 RegisterClassEvent event = new RegisterClassEvent(
334 jdoImplHelper, pcClass, fieldNames, fieldTypes,
335 fieldFlags, persistenceCapableSuperclass);
336 for (Iterator i = listeners.iterator(); i.hasNext();) {
337 RegisterClassListener crl =
338 (RegisterClassListener)i.next();
339 if (crl != null) {
340 crl.registerClass(event);
341 }
342 }
343 }
344 }
345 }
346
347 /***
348 * Unregister metadata by class loader. This method unregisters all
349 * registered <code>PersistenceCapable</code> classes loaded by the
350 * specified class loader. Any attempt to get metadata for unregistered
351 * classes will result in a <code>JDOFatalUserException</code>.
352 * @param cl the class loader.
353 * @since 1.0.2
354 */
355 public void unregisterClasses (ClassLoader cl)
356 {
357 SecurityManager sec = System.getSecurityManager();
358 if (sec != null) {
359
360 sec.checkPermission (JDOPermission.MANAGE_METADATA);
361 }
362 synchronized(registeredClasses) {
363 for (Iterator i = registeredClasses.keySet().iterator(); i.hasNext();) {
364 Class pcClass = (Class)i.next();
365
366
367
368
369
370
371
372 if ((pcClass != null) && (pcClass.getClassLoader() == cl)) {
373
374
375 i.remove();
376 }
377 }
378 }
379 }
380
381 /***
382 * Unregister metadata by class. This method unregisters the specified
383 * class. Any further attempt to get metadata for the specified class will
384 * result in a <code>JDOFatalUserException</code>.
385 * @param pcClass the <code>PersistenceCapable</code> class to be unregistered.
386 * @since 1.0.2
387 */
388 public void unregisterClass (Class pcClass)
389 {
390 if (pcClass == null)
391 throw new NullPointerException(msg.msg("ERR_NullClass"));
392 SecurityManager sec = System.getSecurityManager();
393 if (sec != null) {
394
395 sec.checkPermission (JDOPermission.MANAGE_METADATA);
396 }
397 registeredClasses.remove(pcClass);
398 }
399
400 /***
401 * Add the specified <code>RegisterClassListener</code> to the listener list.
402 * @param crl the listener to be added
403 */
404 public void addRegisterClassListener (RegisterClassListener crl) {
405 HashSet alreadyRegisteredClasses = null;
406 synchronized (listeners) {
407 listeners.add(crl);
408
409
410
411
412
413 alreadyRegisteredClasses = new HashSet (registeredClasses.keySet());
414 }
415
416
417 for (Iterator it = alreadyRegisteredClasses.iterator(); it.hasNext();) {
418 Class pcClass = (Class)it.next();
419 Meta meta = getMeta (pcClass);
420 RegisterClassEvent event = new RegisterClassEvent(
421 this, pcClass, meta.getFieldNames(), meta.getFieldTypes(),
422 meta.getFieldFlags(), meta.getPersistenceCapableSuperclass());
423 crl.registerClass (event);
424 }
425 }
426
427 /***
428 * Remove the specified <code>RegisterClassListener</code> from the listener list.
429 * @param crl the listener to be removed
430 */
431 public void removeRegisterClassListener (RegisterClassListener crl) {
432 synchronized (listeners) {
433 listeners.remove(crl);
434 }
435 }
436
437 /***
438 * Returns a collection of class objects of the registered
439 * persistence-capable classes.
440 * @return registered persistence-capable classes
441 */
442 public Collection getRegisteredClasses() {
443 return Collections.unmodifiableCollection(registeredClasses.keySet());
444 }
445
446 /*** Look up the metadata for a <code>PersistenceCapable</code> class.
447 * @param pcClass the <code>Class</code>.
448 * @return the <code>Meta</code> for the <code>Class</code>.
449 */
450 private static Meta getMeta (Class pcClass) {
451 Meta ret = (Meta) registeredClasses.get (pcClass);
452 if (ret == null) {
453 throw new JDOFatalUserException(
454 msg.msg ("ERR_NoMetadata", pcClass.getName()));
455 }
456 return ret;
457 }
458
459 /*** Register a class authorized to replaceStateManager. The caller of
460 * this method must be authorized for JDOPermission("setStateManager").
461 * During replaceStateManager, a persistence-capable class will call
462 * the corresponding checkAuthorizedStateManager and the class of the
463 * instance of the parameter must have been registered.
464 * @param smClass a Class that is authorized for JDOPermission("setStateManager").
465 * @throws SecurityException if the caller is not authorized for JDOPermission("setStateManager").
466 * @since 1.0.1
467 */
468 public static void registerAuthorizedStateManagerClass (Class smClass)
469 throws SecurityException {
470 if (smClass == null)
471 throw new NullPointerException(msg.msg("ERR_NullClass"));
472 SecurityManager sm = System.getSecurityManager();
473 if (sm != null) {
474 sm.checkPermission(JDOPermission.SET_STATE_MANAGER);
475 }
476 synchronized (authorizedStateManagerClasses) {
477 authorizedStateManagerClasses.put(smClass, null);
478 }
479 }
480
481 /*** Register classes authorized to replaceStateManager. The caller of
482 * this method must be authorized for JDOPermission("setStateManager").
483 * During replaceStateManager, a persistence-capable class will call
484 * the corresponding checkAuthorizedStateManager and the class of the
485 * instance of the parameter must have been registered.
486 * @param smClasses a Collection of Classes that are authorized for JDOPermission("setStateManager").
487 * @throws SecurityException if the caller is not authorized for JDOPermission("setStateManager").
488 * @since 1.0.1
489 */
490 public static void registerAuthorizedStateManagerClasses (Collection smClasses)
491 throws SecurityException {
492 SecurityManager sm = System.getSecurityManager();
493 if (sm != null) {
494 sm.checkPermission(JDOPermission.SET_STATE_MANAGER);
495 synchronized (authorizedStateManagerClasses) {
496 for (Iterator it = smClasses.iterator(); it.hasNext();) {
497 Object smClass = it.next();
498 if (!(smClass instanceof Class)) {
499 throw new ClassCastException(
500 msg.msg("ERR_StateManagerClassCast",
501 smClass.getClass().getName()));
502 }
503 registerAuthorizedStateManagerClass((Class)it.next());
504 }
505 }
506 }
507 }
508
509 /*** Check that the parameter instance is of a class that is authorized for
510 * JDOPermission("setStateManager"). This method is called by the
511 * replaceStateManager method in persistence-capable classes.
512 * A class that is passed as the parameter to replaceStateManager must be
513 * authorized for JDOPermission("setStateManager"). To improve performance,
514 * first the set of authorized classes is checked, and if not present, a
515 * regular permission check is made. The regular permission check requires
516 * that all callers on the stack, including the persistence-capable class
517 * itself, must be authorized for JDOPermission("setStateManager").
518 * @param sm an instance of StateManager whose class is to be checked.
519 * @since 1.0.1
520 */
521 public static void checkAuthorizedStateManager (StateManager sm) {
522 checkAuthorizedStateManagerClass(sm.getClass());
523 }
524
525 /*** Check that the parameter instance is a class that is authorized for
526 * JDOPermission("setStateManager"). This method is called by the
527 * constructors of JDO Reference Implementation classes.
528 * @param smClass a Class to be checked for JDOPermission("setStateManager")
529 * @since 1.0.1
530 */
531 public static void checkAuthorizedStateManagerClass (Class smClass) {
532 final SecurityManager scm = System.getSecurityManager();
533 if (scm == null) {
534
535 return;
536 }
537 synchronized(authorizedStateManagerClasses) {
538 if (authorizedStateManagerClasses.containsKey(smClass)) {
539 return;
540 }
541 }
542
543 scm.checkPermission(JDOPermission.SET_STATE_MANAGER);
544 }
545
546 /***
547 * Construct an instance of a key class using a String as input.
548 * This is a helper interface for use with ObjectIdentity.
549 * Classes without a String constructor (such as those in java.lang
550 * and java.util) will use this interface for constructing new instances.
551 * The result might be a singleton or use some other strategy.
552 */
553 public interface StringConstructor {
554 /***
555 * Construct an instance of the class for which this instance
556 * is registered.
557 * @param s the parameter for construction
558 * @return the constructed object
559 */
560 public Object construct(String s);
561 }
562
563 /***
564 * Special StringConstructor instances for use with specific
565 * classes that have no public String constructor. The Map is
566 * keyed on class instance and the value is an instance of
567 * StringConstructor.
568 */
569 static Map stringConstructorMap = new HashMap();
570
571 /***
572 *
573 * Register special StringConstructor instances. These instances
574 * are for constructing instances from String parameters where there
575 * is no String constructor for them.
576 * @param cls the class to register a StringConstructor for
577 * @param sc the StringConstructor instance
578 * @return the previous StringConstructor registered for this class
579 */
580 public Object registerStringConstructor(Class cls, StringConstructor sc) {
581 synchronized(stringConstructorMap) {
582 return stringConstructorMap.put(cls, sc);
583 }
584 }
585
586 /*** Register the default special StringConstructor instances.
587 */
588 static {
589 JDOImplHelper helper = getInstance();
590 if (isClassLoadable("java.util.Currency")) {
591 helper.registerStringConstructor(Currency.class, new StringConstructor() {
592 public Object construct(String s) {
593 try {
594 return Currency.getInstance(s);
595 } catch (IllegalArgumentException ex) {
596 throw new javax.jdo.JDOUserException(
597 msg.msg("EXC_CurrencyStringConstructorIllegalArgument", s), ex);
598 } catch (Exception ex) {
599 throw new JDOUserException(
600 msg.msg("EXC_CurrencyStringConstructorException"), ex);
601 }
602 }
603 });
604 }
605 helper.registerStringConstructor(Locale.class, new StringConstructor() {
606 public Object construct(String s) {
607 try {
608 return getLocale(s);
609 } catch (Exception ex) {
610 throw new JDOUserException(
611 msg.msg("EXC_LocaleStringConstructorException"), ex);
612 }
613 }
614 });
615 helper.registerStringConstructor(Date.class, new StringConstructor() {
616 public synchronized Object construct(String s) {
617 try {
618
619 return new Date(Long.parseLong(s));
620 } catch (NumberFormatException ex) {
621
622 ParsePosition pp = new ParsePosition(0);
623 Date result = dateFormat.parse(s, pp);
624 if (result == null) {
625 throw new JDOUserException (
626 msg.msg("EXC_DateStringConstructor", new Object[]
627 {s, new Integer(pp.getErrorIndex()), dateFormatPattern}));
628 }
629 return result;
630 }
631 }
632 });
633 }
634
635 /***
636 * Parse the String to a Locale.
637 */
638 private static Locale getLocale(String s) {
639 String lang = s;
640 int firstUnderbar = s.indexOf('_');
641 if (firstUnderbar == -1) {
642
643 return new Locale(lang);
644 }
645 lang = s.substring(0, firstUnderbar);
646 String country;
647 int secondUnderbar = s.indexOf('_', firstUnderbar + 1);
648 if (secondUnderbar == -1) {
649
650 country = s.substring(firstUnderbar + 1);
651 return new Locale(lang, country);
652 }
653 country = s.substring(firstUnderbar + 1, secondUnderbar);
654 String variant = s.substring(secondUnderbar + 1);
655 return new Locale(lang, country, variant);
656 }
657 /***
658 * Determine if a class is loadable in the current environment.
659 */
660 public static boolean isClassLoadable(String className) {
661 try {
662 Class.forName(className);
663 return true;
664 } catch (ClassNotFoundException ex) {
665 return false;
666 }
667 }
668
669 /***
670 * Construct an instance of the parameter class, using the keyString
671 * as an argument to the constructor. If the class has a StringConstructor
672 * instance registered, use it. If not, try to find a constructor for
673 * the class with a single String argument. Otherwise, throw a
674 * JDOUserException.
675 * @param className the name of the class
676 * @param keyString the String parameter for the constructor
677 * @return the result of construction
678 */
679 public static Object construct(String className, String keyString) {
680 StringConstructor stringConstructor;
681 try {
682 Class keyClass = Class.forName(className);
683 synchronized(stringConstructorMap) {
684 stringConstructor =
685 (StringConstructor) stringConstructorMap.get(keyClass);
686 }
687 if (stringConstructor != null) {
688 return stringConstructor.construct(keyString);
689 } else {
690 Constructor keyConstructor =
691 keyClass.getConstructor(new Class[]{String.class});
692 return keyConstructor.newInstance(new Object[]{keyString});
693 }
694 } catch (JDOException ex) {
695 throw ex;
696 } catch (Exception ex) {
697
698
699
700
701
702 throw new JDOUserException(
703 msg.msg("EXC_ObjectIdentityStringConstruction",
704 new Object[] {ex.toString(), className, keyString}), ex);
705 }
706 }
707
708 /***
709 * Register a DateFormat instance for use with constructing Date
710 * instances. The default is the default DateFormat instance.
711 * If the new instance implements SimpleDateFormat, get its pattern
712 * for error messages.
713 * @param df the DateFormat instance to use
714 */
715 public synchronized void registerDateFormat(DateFormat df) {
716 dateFormat = df;
717 if (df instanceof SimpleDateFormat) {
718 dateFormatPattern = ((SimpleDateFormat)df).toPattern();
719 } else {
720 dateFormatPattern = msg.msg("MSG_unknown");
721 }
722 }
723
724 /*** This is a helper class to manage metadata per persistence-capable
725 * class. The information is used at runtime to provide field names and
726 * field types to the JDO Model.
727 *
728 * This is the value of the <code>HashMap</code> which
729 * relates the <code>PersistenceCapable Class</code>
730 * as a key to the metadata.
731 */
732 static class Meta {
733
734 /*** Construct an instance of <code>Meta</code>.
735 * @param fieldNames An array of <code>String</code>
736 * @param fieldTypes An array of <code>Class</code>
737 * @param fieldFlags an array of <code>int</code>
738 * @param persistenceCapableSuperclass the most immediate <code>PersistenceCapable</code> superclass
739 * @param pc An instance of the <code>PersistenceCapable</code> class
740 */
741 Meta (String[] fieldNames, Class[] fieldTypes, byte[] fieldFlags,
742 Class persistenceCapableSuperclass, PersistenceCapable pc) {
743 this.fieldNames = fieldNames;
744 this.fieldTypes = fieldTypes;
745 this.fieldFlags = fieldFlags;
746 this.persistenceCapableSuperclass = persistenceCapableSuperclass;
747 this.pc = pc;
748 }
749
750 /*** This is an array of field names used
751 * for the Model at runtime. The field
752 * is passed by the static class initialization.
753 */
754 String fieldNames[];
755
756 /*** Get the field names from the metadata.
757 * @return the array of field names.
758 */
759 String[] getFieldNames() {
760 return fieldNames;
761 }
762
763 /*** This is an array of field types used
764 * for the Model at runtime. The field
765 * is passed by the static class initialization.
766 */
767 Class fieldTypes[];
768
769 /*** Get the field types from the metadata.
770 * @return the array of field types.
771 */
772 Class[] getFieldTypes() {
773 return fieldTypes;
774 }
775
776 /*** This is an array of field flags used
777 * for the Model at runtime. The field
778 * is passed by the static class initialization.
779 */
780 byte fieldFlags[];
781
782 /*** Get the field types from the metadata.
783 * @return the array of field types.
784 */
785 byte[] getFieldFlags() {
786 return fieldFlags;
787 }
788
789 /*** This is the <code>Class</code> instance of the <code>PersistenceCapable</code> superclass.
790 */
791 Class persistenceCapableSuperclass;
792
793 /*** Return the <code>PersistenceCapable</code> superclass.
794 * @return the <code>PersistenceCapable</code> superclass
795 */
796 Class getPersistenceCapableSuperclass() {
797 return persistenceCapableSuperclass;
798 }
799 /*** This is an instance of <code>PersistenceCapable</code>,
800 * used at runtime to create new instances.
801 */
802 PersistenceCapable pc;
803
804 /*** Get an instance of the <code>PersistenceCapable</code> class.
805 * @return an instance of the <code>PersistenceCapable Class</code>.
806 */
807 PersistenceCapable getPC() {
808 return pc;
809 }
810
811 /*** Return the string form of the metadata.
812 * @return the string form
813 */
814 public String toString() {
815 return "Meta-" + pc.getClass().getName();
816 }
817 }
818
819 /*** Add a StateInterrogation to the list. Create a new list
820 * in case there is an iterator open on the original list.
821 */
822 public synchronized void addStateInterrogation(StateInterrogation si) {
823 List newList = new ArrayList(stateInterrogations);
824 newList.add(si);
825 stateInterrogations = newList;
826 }
827
828 /*** Remove a StateInterrogation from the list. Create a new list
829 * in case there is an iterator open on the original list.
830 */
831 public synchronized void removeStateInterrogation(StateInterrogation si) {
832 List newList = new ArrayList(stateInterrogations);
833 newList.remove(si);
834 stateInterrogations = newList;
835 }
836
837 /*** Return an Iterator over all StateInterrogation instances.
838 * Synchronize to avoid add/remove/iterate conflicts.
839 */
840 private synchronized Iterator getStateInterrogationIterator() {
841 return stateInterrogations.iterator();
842 }
843
844 /*** Mark a non-binary-compatible instance dirty. Delegate to all
845 * registered StateInterrogation instances until one of them
846 * handles the call.
847 */
848 public void nonBinaryCompatibleMakeDirty(Object pc, String fieldName) {
849 Iterator sit = getStateInterrogationIterator();
850 while (sit.hasNext()) {
851 StateInterrogation si = (StateInterrogation)sit.next();
852 if (si.makeDirty(pc, fieldName)) return;
853 }
854 }
855
856 /*** Determine the state of a non-binary-compatible instance.
857 * Delegate to all registered StateInterrogation instances until
858 * one of them handles the call (returns a non-null Boolean
859 * with the answer).
860 * The caller provides the stateless "method object" that does
861 * the actual call to the StateInterrogation instance.
862 */
863 public boolean nonBinaryCompatibleIs(Object pc,
864 StateInterrogationBooleanReturn sibr) {
865 Iterator sit = getStateInterrogationIterator();
866 while (sit.hasNext()) {
867 StateInterrogation si = (StateInterrogation)sit.next();
868 Boolean result = sibr.is(pc, si);
869 if (result != null) return result.booleanValue();
870 }
871 return false;
872 }
873
874 /*** Return an object associated with a non-binary-compatible instance.
875 * Delegate to all registered StateInterrogation instances until
876 * one of them handles the call (returns a non-null answer).
877 * The caller provides the stateless "method object" that does
878 * the actual call to the StateInterrogation instance.
879 */
880 public Object nonBinaryCompatibleGet(Object pc,
881 StateInterrogationObjectReturn sibr) {
882 Iterator sit = getStateInterrogationIterator();
883 while (sit.hasNext()) {
884 StateInterrogation si = (StateInterrogation)sit.next();
885 Object result = sibr.get(pc, si);
886 if (result != null) return result;
887 }
888 return null;
889 }
890
891 /*** This is an interface used to interrogate the state of an instance
892 * that does not implement PersistenceCapable. It is used for the
893 * methods that return a boolean value.
894 */
895 public static interface StateInterrogationBooleanReturn {
896 public Boolean is(Object pc, StateInterrogation si);
897 }
898
899 /*** This is an interface used to interrogate the state of an instance
900 * that does not implement PersistenceCapable. It is used for the
901 * methods that return an Object value.
902 */
903 public static interface StateInterrogationObjectReturn {
904 public Object get(Object pc, StateInterrogation si);
905 }
906 }