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