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