1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.meta;
20
21 import java.io.File;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Modifier;
25 import java.security.AccessController;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Comparator;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.TreeMap;
37
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.openjpa.conf.OpenJPAConfiguration;
40 import org.apache.openjpa.datacache.DataCache;
41 import org.apache.openjpa.enhance.PCRegistry;
42 import org.apache.openjpa.enhance.Reflection;
43 import org.apache.openjpa.enhance.PersistenceCapable;
44 import org.apache.openjpa.lib.log.Log;
45 import org.apache.openjpa.lib.meta.SourceTracker;
46 import org.apache.openjpa.lib.util.J2DoPrivHelper;
47 import org.apache.openjpa.lib.util.Localizer;
48 import org.apache.openjpa.lib.xml.Commentable;
49 import org.apache.openjpa.util.BigDecimalId;
50 import org.apache.openjpa.util.BigIntegerId;
51 import org.apache.openjpa.util.ByteId;
52 import org.apache.openjpa.util.CharId;
53 import org.apache.openjpa.util.DateId;
54 import org.apache.openjpa.util.DoubleId;
55 import org.apache.openjpa.util.FloatId;
56 import org.apache.openjpa.util.GeneralException;
57 import org.apache.openjpa.util.IntId;
58 import org.apache.openjpa.util.InternalException;
59 import org.apache.openjpa.util.LongId;
60 import org.apache.openjpa.util.MetaDataException;
61 import org.apache.openjpa.util.ObjectId;
62 import org.apache.openjpa.util.OpenJPAId;
63 import org.apache.openjpa.util.ShortId;
64 import org.apache.openjpa.util.StringId;
65 import org.apache.openjpa.util.UnsupportedException;
66 import org.apache.openjpa.util.ImplHelper;
67 import serp.util.Strings;
68
69 /**
70 * Contains metadata about a persistent type.
71 * This metadata is available both at enhancement time and runtime.
72 * Note that this class employs aggressive caching, and therefore it is
73 * important to finalize the configuration of field metadatas before invoking
74 * methods that depend on that configuration, such as
75 * {@link #getPrimaryKeyFields}.
76 *
77 * @author Abe White
78 */
79 public class ClassMetaData
80 extends Extensions
81 implements Comparable, SourceTracker, MetaDataContext, MetaDataModes,
82 Commentable {
83
84 /**
85 * Unkonwn identity type.
86 */
87 public static final int ID_UNKNOWN = 0;
88
89 /**
90 * Datastore identity type.
91 */
92 public static final int ID_DATASTORE = 1;
93
94 /**
95 * Application identity type.
96 */
97 public static final int ID_APPLICATION = 2;
98
99 /**
100 * Unknown access type.
101 */
102 public static final int ACCESS_UNKNOWN = 0;
103
104 /**
105 * Persistent attributes are accessed via direct field access. Bit flag.
106 */
107 public static final int ACCESS_FIELD = 2 << 0;
108
109 /**
110 * Persistent attributes are accessed via setters and getters. Bit flag.
111 */
112 public static final int ACCESS_PROPERTY = 2 << 1;
113
114 /**
115 * Value for using a synthetic detached state field, which is the default.
116 */
117 public static final String SYNTHETIC = "`syn";
118
119 protected static final String DEFAULT_STRING = "`";
120
121 private static final Localizer _loc = Localizer.forPackage
122 (ClassMetaData.class);
123
124 private static final FetchGroup[] EMPTY_FETCH_GROUP_ARRAY
125 = new FetchGroup[0];
126 private static final String[] EMPTY_STRING_ARRAY = new String[0];
127
128 private MetaDataRepository _repos;
129 private transient ClassLoader _loader = null;
130
131 private final ValueMetaData _owner;
132 private final LifecycleMetaData _lifeMeta = new LifecycleMetaData(this);
133 private File _srcFile = null;
134 private int _srcType = SRC_OTHER;
135 private String[] _comments = null;
136 private int _listIndex = -1;
137 private int _srcMode = MODE_META | MODE_MAPPING;
138 private int _resMode = MODE_NONE;
139
140 private Class _type = Object.class;
141 private final Map _fieldMap = new TreeMap();
142 private Map _supFieldMap = null;
143 private boolean _defSupFields = false;
144 private Collection _staticFields = null;
145 private int[] _fieldDataTable = null;
146 private Map _fgMap = null;
147
148 ////////////////////////////////////////////////////////////////////
149 // Note: if you add additional state, make sure to add it to copy()
150 ////////////////////////////////////////////////////////////////////
151
152 private Class _objectId = null;
153 private Boolean _objectIdShared = null;
154 private Boolean _openjpaId = null;
155 private Boolean _extent = null;
156 private Boolean _embedded = null;
157 private Boolean _interface = null;
158 private Class _impl = null;
159 private List _interfaces = null;
160 private final Map _ifaceMap = new HashMap();
161 private int _identity = ID_UNKNOWN;
162 private int _idStrategy = ValueStrategies.NONE;
163 private int _accessType = ACCESS_UNKNOWN;
164
165 private String _seqName = DEFAULT_STRING;
166 private SequenceMetaData _seqMeta = null;
167 private String _cacheName = DEFAULT_STRING;
168 private int _cacheTimeout = Integer.MIN_VALUE;
169 private Boolean _detachable = null;
170 private String _detachState = DEFAULT_STRING;
171 private String _alias = null;
172 private int _versionIdx = Integer.MIN_VALUE;
173
174 private Class _super = null;
175 private ClassMetaData _superMeta = null;
176 private Class[] _subs = null;
177 private ClassMetaData[] _subMetas = null;
178 private ClassMetaData[] _mapSubMetas = null;
179
180 private FieldMetaData[] _fields = null;
181 private FieldMetaData[] _unmgdFields = null;
182 private FieldMetaData[] _allFields = null;
183 private FieldMetaData[] _allPKFields = null;
184 private FieldMetaData[] _allDFGFields = null;
185 private FieldMetaData[] _definedFields = null;
186 private FieldMetaData[] _listingFields = null;
187 private FieldMetaData[] _allListingFields = null;
188 private FetchGroup[] _fgs = null;
189 private FetchGroup[] _customFGs = null;
190 private boolean _intercepting = false;
191
192 /**
193 * Constructor. Supply described type and repository.
194 */
195 protected ClassMetaData(Class type, MetaDataRepository repos) {
196 _repos = repos;
197 _owner = null;
198 setDescribedType(type);
199 }
200
201 /**
202 * Embedded constructor. Supply embedding value.
203 */
204 protected ClassMetaData(ValueMetaData owner) {
205 _owner = owner;
206 _repos = owner.getRepository();
207 setEnvClassLoader(owner.getFieldMetaData().getDefiningMetaData().
208 getEnvClassLoader());
209 }
210
211 /**
212 * Return the owning repository.
213 */
214 public MetaDataRepository getRepository() {
215 return _repos;
216 }
217
218 /**
219 * If this metadata is for an embedded object, returning the owning value.
220 */
221 public ValueMetaData getEmbeddingMetaData() {
222 return _owner;
223 }
224
225 /**
226 * The persistence capable class described by this metadata.
227 */
228 public Class getDescribedType() {
229 return _type;
230 }
231
232 /**
233 * Set the class descibed by this metadata. The type may be reset when
234 * an embedded value changes its declared type.
235 */
236 protected void setDescribedType(Class type) {
237 if (type.getSuperclass() != null && "java.lang.Enum".equals
238 (type.getSuperclass().getName()))
239 throw new MetaDataException(_loc.get("enum", type));
240 _type = type;
241 if (PersistenceCapable.class.isAssignableFrom(type))
242 setIntercepting(true);
243 }
244
245 /**
246 * The environmental loader used when loading this metadata.
247 * The class metadata should use this loader when loading metadata for
248 * its superclass and field types.
249 */
250 public ClassLoader getEnvClassLoader() {
251 return _loader;
252 }
253
254 /**
255 * The class environmental loader used when loading this metadata.
256 * The class metadata should use this loader when loading metadata for
257 * its superclass and field types.
258 */
259 public void setEnvClassLoader(ClassLoader loader) {
260 _loader = loader;
261 }
262
263 /**
264 * The persistence capable superclass of the described type.
265 */
266 public Class getPCSuperclass() {
267 return _super;
268 }
269
270 /**
271 * The persistence capable superclass of the described type.
272 */
273 public void setPCSuperclass(Class pc) {
274 clearAllFieldCache();
275 _super = pc;
276 }
277
278 /**
279 * The metadata for this class' superclass.
280 */
281 public ClassMetaData getPCSuperclassMetaData() {
282 if (_superMeta == null && _super != null) {
283 if (_owner != null) {
284 _superMeta = _repos.newEmbeddedClassMetaData(_owner);
285 _superMeta.setDescribedType(_super);
286 } else
287 _superMeta = _repos.getMetaData(_super, _loader, true);
288 }
289 return _superMeta;
290 }
291
292 /**
293 * The metadata for this class' superclass.
294 */
295 public void setPCSuperclassMetaData(ClassMetaData meta) {
296 clearAllFieldCache();
297 _superMeta = meta;
298 if (meta != null)
299 setPCSuperclass(meta.getDescribedType());
300 }
301
302 /**
303 * Whether this class is mapped to the datastore. By default, only
304 * returns false if class is embedded-only, but subclasses might override
305 * to allow unmapped other types.
306 */
307 public boolean isMapped() {
308 return _embedded != Boolean.TRUE;
309 }
310
311 /**
312 * Return the closest mapped superclass.
313 */
314 public ClassMetaData getMappedPCSuperclassMetaData() {
315 ClassMetaData sup = getPCSuperclassMetaData();
316 if (sup == null || sup.isMapped())
317 return sup;
318 return sup.getMappedPCSuperclassMetaData();
319 }
320
321 /**
322 * Return the known persistence capable subclasses of the described type,
323 * or empty array if none or if this is embedded metadata.
324 */
325 public Class[] getPCSubclasses() {
326 if (_owner != null)
327 return _repos.EMPTY_CLASSES;
328
329 _repos.processRegisteredClasses(_loader);
330 if (_subs == null) {
331 Collection subs = _repos.getPCSubclasses(_type);
332 _subs = (Class[]) subs.toArray(new Class[subs.size()]);
333 }
334 return _subs;
335 }
336
337 /**
338 * Return the metadata for the known persistence capable subclasses of
339 * the described type, or empty array if none or if this is embedded
340 * metadata.
341 */
342 public ClassMetaData[] getPCSubclassMetaDatas() {
343 if (_owner != null)
344 return _repos.EMPTY_METAS;
345
346 Class[] subs = getPCSubclasses(); // checks for new
347 if (_subMetas == null) {
348 if (subs.length == 0)
349 _subMetas = _repos.EMPTY_METAS;
350 else {
351 ClassMetaData[] metas = _repos.newClassMetaDataArray
352 (subs.length);
353 for (int i = 0; i < subs.length; i++)
354 metas[i] = _repos.getMetaData(subs[i], _loader, true);
355 _subMetas = metas;
356 }
357 }
358 return _subMetas;
359 }
360
361 /**
362 * Return all mapped subclasses.
363 */
364 public ClassMetaData[] getMappedPCSubclassMetaDatas() {
365 if (_owner != null)
366 return _repos.EMPTY_METAS;
367
368 ClassMetaData[] subs = getPCSubclassMetaDatas(); // checks for new
369 if (_mapSubMetas == null) {
370 if (subs.length == 0)
371 _mapSubMetas = subs;
372 else {
373 List mapped = new ArrayList(subs.length);
374 for (int i = 0; i < subs.length; i++)
375 if (subs[i].isMapped())
376 mapped.add(subs[i]);
377 _mapSubMetas = (ClassMetaData[]) mapped.toArray
378 (_repos.newClassMetaDataArray(mapped.size()));
379 }
380 }
381 return _mapSubMetas;
382 }
383
384 /**
385 * The type of identity being used. This will be one of:
386 * <ul>
387 * <li>{@link #ID_UNKNOWN}: unknown identity type</li>
388 * <li>{@link #ID_DATASTORE}: identity managed by the data store and
389 * independent of the fields of the instance</li>
390 * <li>{@link #ID_APPLICATION}: identity managed by the application and
391 * defined by one or more fields of the instance</li>
392 * </ul> If unspecified, defaults to {@link #ID_DATASTORE} if there are no
393 * primary key fields, and {@link #ID_APPLICATION} otherwise.
394 */
395 public int getIdentityType() {
396 if (_identity == ID_UNKNOWN) {
397 ClassMetaData sup = getPCSuperclassMetaData();
398 if (sup != null && sup.getIdentityType() != ID_UNKNOWN)
399 _identity = sup.getIdentityType();
400 else if (getPrimaryKeyFields().length > 0)
401 _identity = ID_APPLICATION;
402 else if (isMapped())
403 _identity = ID_DATASTORE;
404 else
405 _identity = _repos.getMetaDataFactory().getDefaults().
406 getDefaultIdentityType();
407 }
408 return _identity;
409 }
410
411 /**
412 * The type of identity being used. This will be one of:
413 * <ul>
414 * <li>{@link #ID_UNKNOWN}: unknown identity type</li>
415 * <li>{@link #ID_DATASTORE}: identity managed by the data store and
416 * independent of the fields of the instance</li>
417 * <li>{@link #ID_APPLICATION}: identity managed by the application and
418 * defined by one or more fields of the instance</li>
419 * </ul> If unspecified, defaults to {@link #ID_DATASTORE} if there are no
420 * primary key fields, and {@link #ID_APPLICATION} otherwise.
421 */
422 public void setIdentityType(int type) {
423 _identity = type;
424 if (type != ID_APPLICATION) {
425 _objectId = null;
426 _openjpaId = null;
427 }
428 }
429
430 /**
431 * The metadata-specified class to use for the object ID.
432 */
433 public Class getObjectIdType() {
434 if (_objectId != null)
435 return _objectId;
436 if (getIdentityType() != ID_APPLICATION)
437 return null;
438 ClassMetaData sup = getPCSuperclassMetaData();
439 if (sup != null && sup.getIdentityType() != ID_UNKNOWN) {
440 _objectId = sup.getObjectIdType();
441 return _objectId;
442 }
443
444 // figure out openjpa identity type based on primary key field
445 FieldMetaData[] pks = getPrimaryKeyFields();
446 if (pks.length != 1)
447 return null;
448 switch (pks[0].getObjectIdFieldTypeCode()) {
449 case JavaTypes.BYTE:
450 case JavaTypes.BYTE_OBJ:
451 _objectId = ByteId.class;
452 break;
453 case JavaTypes.CHAR:
454 case JavaTypes.CHAR_OBJ:
455 _objectId = CharId.class;
456 break;
457 case JavaTypes.DOUBLE:
458 case JavaTypes.DOUBLE_OBJ:
459 _objectId = DoubleId.class;
460 break;
461 case JavaTypes.FLOAT:
462 case JavaTypes.FLOAT_OBJ:
463 _objectId = FloatId.class;
464 break;
465 case JavaTypes.INT:
466 case JavaTypes.INT_OBJ:
467 _objectId = IntId.class;
468 break;
469 case JavaTypes.LONG:
470 case JavaTypes.LONG_OBJ:
471 _objectId = LongId.class;
472 break;
473 case JavaTypes.SHORT:
474 case JavaTypes.SHORT_OBJ:
475 _objectId = ShortId.class;
476 break;
477 case JavaTypes.STRING:
478 _objectId = StringId.class;
479 break;
480 case JavaTypes.DATE:
481 _objectId = DateId.class;
482 break;
483 case JavaTypes.OID:
484 case JavaTypes.OBJECT:
485 _objectId = ObjectId.class;
486 break;
487 case JavaTypes.BIGDECIMAL:
488 _objectId = BigDecimalId.class;
489 break;
490 case JavaTypes.BIGINTEGER:
491 _objectId = BigIntegerId.class;
492 break;
493 }
494 return _objectId;
495 }
496
497 /**
498 * The metadata-specified class to use for the object ID.
499 */
500 public void setObjectIdType(Class cls, boolean shared) {
501 _objectId = null;
502 _openjpaId = null;
503 _objectIdShared = null;
504 if (cls != null) {
505 // don't let people assign OpenJPAId types; safer to calculate it
506 // ourselves
507 setIdentityType(ID_APPLICATION);
508 if (!OpenJPAId.class.isAssignableFrom(cls)) {
509 _objectId = cls;
510 _objectIdShared = (shared) ? Boolean.TRUE : Boolean.FALSE;
511 }
512 }
513 }
514
515 /**
516 * Whether this type uses an application identity class that is shared
517 * with other classes, and is therefore wrapped in an {@link ObjectId}.
518 */
519 public boolean isObjectIdTypeShared() {
520 if (_objectIdShared != null)
521 return _objectIdShared.booleanValue();
522 if (_super != null)
523 return getPCSuperclassMetaData().isObjectIdTypeShared();
524 return isOpenJPAIdentity();
525 }
526
527 /**
528 * Whether this type uses OpenJPA identity.
529 */
530 public boolean isOpenJPAIdentity() {
531 if (_openjpaId == null) {
532 Class cls = getObjectIdType();
533 if (cls == null)
534 return false;
535 _openjpaId = (OpenJPAId.class.isAssignableFrom(cls)) ? Boolean.TRUE
536 : Boolean.FALSE;
537 }
538 return _openjpaId.booleanValue();
539 }
540
541 /**
542 * The strategy to use for datastore identity generation.
543 * One of the constants from {@link ValueStrategies}.
544 */
545 public int getIdentityStrategy() {
546 if (getIdentityType() == ID_DATASTORE
547 && _idStrategy == ValueStrategies.NONE) {
548 ClassMetaData sup = getPCSuperclassMetaData();
549 if (sup != null && sup.getIdentityType() != ID_UNKNOWN)
550 _idStrategy = sup.getIdentityStrategy();
551 else
552 _idStrategy = ValueStrategies.NATIVE;
553 }
554 return _idStrategy;
555 }
556
557 /**
558 * The strategy to use for datastore identity generation.
559 * One of the constants from {@link ValueStrategies}.
560 */
561 public void setIdentityStrategy(int strategy) {
562 _idStrategy = strategy;
563 if (strategy != ValueStrategies.SEQUENCE)
564 setIdentitySequenceName(null);
565 }
566
567 /**
568 * The datastore identity sequence name, or null for none.
569 */
570 public String getIdentitySequenceName() {
571 if (DEFAULT_STRING.equals(_seqName)) {
572 if (_super != null)
573 _seqName = getPCSuperclassMetaData().getIdentitySequenceName();
574 else
575 _seqName = null;
576 }
577 return _seqName;
578 }
579
580 /**
581 * The datastore identity sequence name, or null for none.
582 */
583 public void setIdentitySequenceName(String seqName) {
584 _seqName = seqName;
585 _seqMeta = null;
586 if (seqName != null)
587 setIdentityStrategy(ValueStrategies.SEQUENCE);
588 }
589
590 /**
591 * Metadata for the datastore identity sequence.
592 */
593 public SequenceMetaData getIdentitySequenceMetaData() {
594 if (_seqMeta == null && getIdentitySequenceName() != null)
595 _seqMeta = _repos.getSequenceMetaData(this,
596 getIdentitySequenceName(), true);
597 return _seqMeta;
598 }
599
600 /**
601 * Information about lifecycle callbacks for this class.
602 */
603 public LifecycleMetaData getLifecycleMetaData() {
604 return _lifeMeta;
605 }
606
607 /**
608 * Returns the alias for the described type, or <code>null</code> if none
609 * has been set.
610 *
611 * @see #setTypeAlias
612 */
613 public String getTypeAlias() {
614 if (_alias == null)
615 _alias = Strings.getClassName(_type);
616 return _alias;
617 }
618
619 /**
620 * Sets the alias for the described type. The alias can be
621 * any arbitrary string that the implementation can later use to
622 * refer to the class. Note that at runtime, only the alias
623 * computed when the persistent type was enhanced is used.
624 *
625 * @param alias the alias name to apply to the described type
626 */
627 public void setTypeAlias(String alias) {
628 _alias = alias;
629 }
630
631 /**
632 * The access type used by this class. Either {@link #ACCESS_FIELD}
633 * or {@link #ACCESS_PROPERTY}.
634 */
635 public int getAccessType() {
636 if (_accessType == ACCESS_UNKNOWN) {
637 ClassMetaData sup = getPCSuperclassMetaData();
638 if (sup != null)
639 return sup.getAccessType();
640 else {
641 return getRepository().getMetaDataFactory().
642 getDefaults().getDefaultAccessType();
643 }
644 }
645 return _accessType;
646 }
647
648 /**
649 * The access type used by this class. Must be either
650 * {@link #ACCESS_FIELD} or {@link #ACCESS_PROPERTY}.
651 */
652 public void setAccessType(int type) {
653 _accessType = type;
654 }
655
656 /**
657 * Whether the type requires extent management.
658 */
659 public boolean getRequiresExtent() {
660 if (_owner != null || isEmbeddedOnly())
661 return false;
662
663 if (_extent == null) {
664 ClassMetaData sup = getPCSuperclassMetaData();
665 if (sup != null)
666 _extent = (sup.getRequiresExtent()) ? Boolean.TRUE
667 : Boolean.FALSE;
668 else
669 _extent = Boolean.TRUE;
670 }
671 return _extent.booleanValue();
672 }
673
674 /**
675 * Whether the type requires extent management.
676 */
677 public void setRequiresExtent(boolean req) {
678 _extent = (req) ? Boolean.TRUE : Boolean.FALSE;
679 }
680
681 /**
682 * Whether the type can only be used as an embedded object.
683 */
684 public boolean isEmbeddedOnly() {
685 if (_embedded == null) {
686 ClassMetaData sup = getPCSuperclassMetaData();
687 if (sup != null)
688 _embedded = (sup.isEmbeddedOnly()) ? Boolean.TRUE
689 : Boolean.FALSE;
690 else
691 _embedded = Boolean.FALSE;
692 }
693 return _embedded.booleanValue();
694 }
695
696 /**
697 * Whether the type can only be used as an embedded object.
698 */
699 public void setEmbeddedOnly(boolean embed) {
700 _embedded = (embed) ? Boolean.TRUE : Boolean.FALSE;
701 }
702
703 /**
704 * Whether the type's fields are actively intercepted, either by
705 * redefinition or enhancement.
706 */
707 public boolean isIntercepting() {
708 return _intercepting;
709 }
710
711 /**
712 * Whether the type's fields are actively intercepted, either by
713 * redefinition or enhancement.
714 */
715 public void setIntercepting(boolean intercepting) {
716 _intercepting = intercepting;
717 }
718
719 /**
720 * Whether the type is a managed interface.
721 */
722 public boolean isManagedInterface() {
723 if (!_type.isInterface())
724 return false;
725 return _interface == null ? false : _interface.booleanValue();
726 }
727
728 /**
729 * Whether the type is a managed interface
730 */
731 public void setManagedInterface(boolean managedInterface) {
732 if (!_type.isInterface())
733 throw new MetaDataException(_loc.get("not-interface", _type));
734 _interface = managedInterface ? Boolean.TRUE : Boolean.FALSE;
735
736 // managed interfaces always do proper interception; OpenJPA generates
737 // the implementations.
738 if (isManagedInterface())
739 setIntercepting(true);
740
741 // managed interfaces always use property access.
742 setAccessType(ACCESS_PROPERTY);
743 }
744
745 /**
746 * Return the managed interface implementor if any.
747 */
748 public Class getInterfaceImpl() {
749 return _impl;
750 }
751
752 /**
753 * Set the managed interface implementor class.
754 */
755 public void setInterfaceImpl(Class impl) {
756 _impl = impl;
757 }
758
759 /**
760 * Return all explicitly declared interfaces this class implements.
761 */
762 public Class[] getDeclaredInterfaces() {
763 if (_interfaces == null)
764 return MetaDataRepository.EMPTY_CLASSES;
765 return (Class[]) _interfaces.toArray(new Class[_interfaces.size()]);
766 }
767
768 /**
769 * Explicitly declare the given interface among the ones this
770 * class implements.
771 */
772 public void addDeclaredInterface(Class iface) {
773 if (iface == null || !iface.isInterface())
774 throw new MetaDataException(_loc.get("declare-non-interface",
775 this, iface));
776 if (_interfaces == null)
777 _interfaces = new ArrayList();
778 _interfaces.add(iface);
779 }
780
781 /**
782 * Remove the given interface from the declared list.
783 */
784 public boolean removeDeclaredInterface(Class iface) {
785 if (_interfaces == null)
786 return false;
787 return _interfaces.remove(iface);
788 }
789
790 /**
791 * Alias properties from the given interface during queries to
792 * the local field.
793 */
794 public void setInterfacePropertyAlias(Class iface, String orig,
795 String local) {
796 synchronized (_ifaceMap) {
797 Map fields = (Map) _ifaceMap.get(iface);
798 if (fields == null) {
799 fields = new HashMap();
800 _ifaceMap.put(iface, fields);
801 }
802 if (fields.containsKey(orig))
803 throw new MetaDataException(_loc.get("duplicate-iface-alias",
804 this, orig, local));
805 fields.put(orig, local);
806 }
807 }
808
809 /**
810 * Get local field alias for the given interface property.
811 */
812 public String getInterfacePropertyAlias(Class iface, String orig) {
813 synchronized (_ifaceMap) {
814 Map fields = (Map) _ifaceMap.get(iface);
815 if (fields == null)
816 return null;
817 return (String) fields.get(orig);
818 }
819 }
820
821 /**
822 * Return all aliases property named for the given interface.
823 */
824 public String[] getInterfaceAliasedProperties(Class iface) {
825 synchronized (_ifaceMap) {
826 Map fields = (Map) _ifaceMap.get(iface);
827 if (fields == null)
828 return EMPTY_STRING_ARRAY;
829 return (String[]) fields.keySet().toArray(
830 new String[fields.size()]);
831 }
832 }
833
834 /**
835 * Return the number of fields that use impl or intermediate data, in
836 * order to create a compacted array for storage of said data.
837 */
838 public int getExtraFieldDataLength() {
839 int[] table = getExtraFieldDataTable();
840 for (int i = table.length - 1; i >= 0; i--)
841 if (table[i] != -1)
842 return table[i] + 1;
843 return 0;
844 }
845
846 /**
847 * Return the impl / intermediate field data index of the given field
848 * in the compacted array, or -1 if the field does not use extra data.
849 *
850 * @see #getExtraFieldDataLength
851 */
852 public int getExtraFieldDataIndex(int field) {
853 return getExtraFieldDataTable()[field];
854 }
855
856 /**
857 * Creates a table mapping each field index to its extra data index.
858 */
859 private int[] getExtraFieldDataTable() {
860 if (_fieldDataTable == null) {
861 FieldMetaData[] fmds = getFields();
862 int[] table = new int[fmds.length];
863 int idx = 0;
864 for (int i = 0; i < fmds.length; i++) {
865 if (fmds[i].usesIntermediate()
866 || fmds[i].usesImplData() != Boolean.FALSE)
867 table[i] = idx++;
868 else
869 table[i] = -1;
870 }
871 _fieldDataTable = table;
872 }
873 return _fieldDataTable;
874 }
875
876 /**
877 * Return whether the given name represents a managed or static field of
878 * this class, including superclass fields.
879 */
880 public boolean isAccessibleField(String field) {
881 if (getDeclaredField(field) != null)
882 return true;
883 if (_staticFields == null) {
884 Field[] fields = (Field[]) AccessController.doPrivileged(
885 J2DoPrivHelper.getDeclaredFieldsAction(_type));
886 Set names = new HashSet((int) (fields.length * 1.33 + 1));
887 for (int i = 0; i < fields.length; i++)
888 if (Modifier.isStatic(fields[i].getModifiers()))
889 names.add(fields[i].getName());
890 _staticFields = names;
891 }
892 if (_staticFields.contains(field))
893 return true;
894 if (_super != null)
895 return getPCSuperclassMetaData().isAccessibleField(field);
896 return false;
897 }
898
899 /**
900 * Return all field metadatas, including superclass fields.
901 */
902 public FieldMetaData[] getFields() {
903 if (_allFields == null) {
904 if (_super == null)
905 _allFields = getDeclaredFields();
906 else {
907 FieldMetaData[] fields = getDeclaredFields();
908 FieldMetaData[] supFields = getPCSuperclassMetaData().
909 getFields();
910
911 FieldMetaData[] allFields = _repos.newFieldMetaDataArray
912 (fields.length + supFields.length);
913 System.arraycopy(supFields, 0, allFields, 0, supFields.length);
914 replaceDefinedSuperclassFields(allFields, supFields.length);
915
916 for (int i = 0; i < fields.length; i++) {
917 fields[i].setIndex(supFields.length + i);
918 allFields[supFields.length + i] = fields[i];
919 }
920 _allFields = allFields;
921 }
922 }
923 return _allFields;
924 }
925
926 /**
927 * Replace superclass fields that we define with our version.
928 */
929 private void replaceDefinedSuperclassFields(FieldMetaData[] fields,
930 int len) {
931 if (_supFieldMap == null || !_defSupFields)
932 return;
933
934 // don't assume fields are in order; this method is used for
935 // listing order as well
936 FieldMetaData supField;
937 for (int i = 0; i < len; i++) {
938 supField = (FieldMetaData) _supFieldMap.get(fields[i].getName());
939 if (supField != null) {
940 fields[i] = supField;
941 supField.setIndex(i);
942 }
943 }
944 }
945
946 /**
947 * Return the superclass copy of the given field.
948 */
949 protected FieldMetaData getSuperclassField(FieldMetaData supField) {
950 ClassMetaData sm = getPCSuperclassMetaData();
951 FieldMetaData fmd = sm == null ? null : sm.getField(supField.getName());
952 if (fmd == null || fmd.getManagement() != fmd.MANAGE_PERSISTENT)
953 throw new MetaDataException(_loc.get("unmanaged-sup-field",
954 supField, this));
955 return fmd;
956 }
957
958 /**
959 * Return only the fields for this class, without superclass fields.
960 */
961 public FieldMetaData[] getDeclaredFields() {
962 if (_fields == null) {
963 List fields = new ArrayList(_fieldMap.size());
964 FieldMetaData fmd;
965 for (Iterator itr = _fieldMap.values().iterator(); itr.hasNext();) {
966 fmd = (FieldMetaData) itr.next();
967 if (fmd.getManagement() != FieldMetaData.MANAGE_NONE) {
968 fmd.setDeclaredIndex(fields.size());
969 fmd.setIndex(fmd.getDeclaredIndex());
970 fields.add(fmd);
971 }
972 }
973 _fields = (FieldMetaData[]) fields.toArray
974 (_repos.newFieldMetaDataArray(fields.size()));
975 }
976 return _fields;
977 }
978
979 /**
980 * Return primary key fields, or empty array if none. The order
981 * in which the keys are returned will be the order in which
982 * the fields are declared, starting at the least-derived superclass
983 * and ending with the primary key fields of the most-derived subclass.
984 */
985 public FieldMetaData[] getPrimaryKeyFields() {
986 // check for pk fields even if not set to ID_APPLICATION so that
987 // app id tool sees them even when user doesn't declare app id
988 if (_allPKFields == null) {
989 FieldMetaData[] fields = getFields();
990 int num = 0;
991 for (int i = 0; i < fields.length; i++)
992 if (fields[i].isPrimaryKey())
993 num++;
994
995 if (num == 0)
996 _allPKFields = _repos.EMPTY_FIELDS;
997 else {
998 FieldMetaData[] pks = _repos.newFieldMetaDataArray(num);
999 num = 0;
1000 for (int i = 0; i < fields.length; i++) {
1001 if (fields[i].isPrimaryKey()) {
1002 fields[i].setPrimaryKeyIndex(num);
1003 pks[num] = fields[i];
1004 num++;
1005 }
1006 }
1007 _allPKFields = pks;
1008 }
1009 }
1010 return _allPKFields;
1011 }
1012
1013 /**
1014 * Return the list of fields in the default fetch group,
1015 * including superclass fields, or an empty array if none.
1016 */
1017 public FieldMetaData[] getDefaultFetchGroupFields() {
1018 if (_allDFGFields == null) {
1019 FieldMetaData[] fields = getFields();
1020 int num = 0;
1021 for (int i = 0; i < fields.length; i++)
1022 if (fields[i].isInDefaultFetchGroup())
1023 num++;
1024
1025 FieldMetaData[] dfgs = _repos.newFieldMetaDataArray(num);
1026 num = 0;
1027 for (int i = 0; i < fields.length; i++)
1028 if (fields[i].isInDefaultFetchGroup())
1029 dfgs[num++] = fields[i];
1030 _allDFGFields = dfgs;
1031 }
1032 return _allDFGFields;
1033 }
1034
1035 /**
1036 * Return the version field for this class, if any.
1037 */
1038 public FieldMetaData getVersionField() {
1039 if (_versionIdx == Integer.MIN_VALUE) {
1040 FieldMetaData[] fields = getFields();
1041 int idx = -1;
1042 for (int i = 0; i < fields.length; i++) {
1043 if (fields[i].isVersion()) {
1044 if (idx != -1)
1045 throw new MetaDataException(_loc.get
1046 ("mult-vers-fields", this, fields[idx], fields[i]));
1047 idx = i;
1048 }
1049 }
1050 _versionIdx = idx;
1051 }
1052 if (_versionIdx == -1)
1053 return null;
1054 return getFields()[_versionIdx];
1055 }
1056
1057 /**
1058 * Return the metadata for the persistent or transactional field with
1059 * the given absolute index.
1060 *
1061 * @return the field's metadata, or null if not found
1062 */
1063 public FieldMetaData getField(int index) {
1064 FieldMetaData[] fields = getFields();
1065 if (index < 0 || index >= fields.length)
1066 return null;
1067 return fields[index];
1068 }
1069
1070 /**
1071 * Return the metadata for the persistent or transactional field with
1072 * the given relative index.
1073 *
1074 * @return the field's metadata, or null if not found
1075 */
1076 public FieldMetaData getDeclaredField(int index) {
1077 FieldMetaData[] fields = getDeclaredFields();
1078 if (index < 0 || index >= fields.length)
1079 return null;
1080 return fields[index];
1081 }
1082
1083 /**
1084 * Return the metadata for the persistent or transactional field with
1085 * the given name.
1086 *
1087 * @return the field's metadata, or null if not found
1088 */
1089 public FieldMetaData getField(String name) {
1090 FieldMetaData fmd = getDeclaredField(name);
1091 if (fmd != null)
1092 return fmd;
1093 if (_supFieldMap != null && _defSupFields) {
1094 fmd = (FieldMetaData) _supFieldMap.get(name);
1095 if (fmd != null)
1096 return fmd;
1097 }
1098 if (_super != null)
1099 return getPCSuperclassMetaData().getField(name);
1100 return null;
1101 }
1102
1103 /**
1104 * Return the metadata for the persistent or transactional field with
1105 * the given name, without including superclass fields.
1106 *
1107 * @return the field's metadata, or null if not found
1108 */
1109 public FieldMetaData getDeclaredField(String name) {
1110 FieldMetaData field = (FieldMetaData) _fieldMap.get(name);
1111 if (field == null || field.getManagement() == field.MANAGE_NONE)
1112 return null;
1113 return field;
1114 }
1115
1116 /**
1117 * Return any fields that were added as non-managed.
1118 * All other methods to get fields return only those that are managed.
1119 */
1120 public FieldMetaData[] getDeclaredUnmanagedFields() {
1121 if (_unmgdFields == null) {
1122 Collection unmanaged = new ArrayList(3);
1123 FieldMetaData field;
1124 for (Iterator itr = _fieldMap.values().iterator(); itr.hasNext();) {
1125 field = (FieldMetaData) itr.next();
1126 if (field.getManagement() == FieldMetaData.MANAGE_NONE)
1127 unmanaged.add(field);
1128 }
1129 _unmgdFields = (FieldMetaData[]) unmanaged.toArray
1130 (_repos.newFieldMetaDataArray(unmanaged.size()));
1131 }
1132 return _unmgdFields;
1133 }
1134
1135 /**
1136 * Add a new field metadata to this class.
1137 */
1138 public FieldMetaData addDeclaredField(String name, Class type) {
1139 FieldMetaData fmd = _repos.newFieldMetaData(name, type, this);
1140 clearFieldCache();
1141 _fieldMap.put(name, fmd);
1142 return fmd;
1143 }
1144
1145 /**
1146 * Remove the given field from management.
1147 *
1148 * @return true if the field was removed, false otherwise
1149 */
1150 public boolean removeDeclaredField(FieldMetaData field) {
1151 if (field != null && _fieldMap.remove(field.getName()) != null) {
1152 clearFieldCache();
1153 return true;
1154 }
1155 return false;
1156 }
1157
1158 /**
1159 * Return the defined superclass field with the given name, or null if none.
1160 */
1161 public FieldMetaData getDefinedSuperclassField(String name) {
1162 if (_supFieldMap == null)
1163 return null;
1164 return (FieldMetaData) _supFieldMap.get(name);
1165 }
1166
1167 /**
1168 * Add a new defined superclass field metadata to this class.
1169 */
1170 public FieldMetaData addDefinedSuperclassField(String name, Class type,
1171 Class sup) {
1172 FieldMetaData fmd = _repos.newFieldMetaData(name, type, this);
1173 fmd.setDeclaringType(sup);
1174 clearAllFieldCache();
1175 _defSupFields = false;
1176 if (_supFieldMap == null)
1177 _supFieldMap = new HashMap();
1178 _supFieldMap.put(name, fmd);
1179 return fmd;
1180 }
1181
1182 /**
1183 * Remove the given field from management.
1184 *
1185 * @return true if the field was removed, false otherwise
1186 */
1187 public boolean removeDefinedSuperclassField(FieldMetaData field) {
1188 if (field != null && _supFieldMap != null
1189 && _supFieldMap.remove(field.getName()) != null) {
1190 clearAllFieldCache();
1191 _defSupFields = false;
1192 return true;
1193 }
1194 return false;
1195 }
1196
1197 /**
1198 * Incorporate superclass fields redefined in this subclass into this
1199 * metadata. This method is generally called after metadata is resolved
1200 * and mapping information is loaded, but before mapping resolve.
1201 *
1202 * @param force whether to force re-mapping of even mapped superclass fields
1203 */
1204 public void defineSuperclassFields(boolean force) {
1205 if (_defSupFields)
1206 return;
1207
1208 ClassMetaData sup = getPCSuperclassMetaData();
1209 if (isMapped() && sup != null) {
1210 // redefine all unmapped superclass fields
1211 FieldMetaData[] sups = sup.getFields();
1212 for (int i = 0; i < sups.length; i++) {
1213 if ((force || !sups[i].getDefiningMetaData().isMapped())
1214 && getDefinedSuperclassField(sups[i].getName()) == null) {
1215 addDefinedSuperclassField(sups[i].getName(),
1216 sups[i].getDeclaredType(), sups[i].getDeclaringType());
1217 }
1218 }
1219 }
1220 resolveDefinedSuperclassFields();
1221
1222 // this ensures that all field indexes get set when fields are cached.
1223 // I don't like doing this twice (it's also done in resolveMeta), but
1224 // we have to re-cache in case this class or any superclass replaced
1225 // some fields with redefined versions, and I don't want outside code
1226 // to have to call this method after resolve just to get field indexes,
1227 // etc set correctly
1228 clearAllFieldCache();
1229 cacheFields();
1230 }
1231
1232 /**
1233 * Resolve superclass fields we've redefined.
1234 */
1235 private void resolveDefinedSuperclassFields() {
1236 _defSupFields = true;
1237 if (_supFieldMap == null)
1238 return;
1239
1240 FieldMetaData fmd;
1241 FieldMetaData sup;
1242 for (Iterator itr = _supFieldMap.values().iterator(); itr.hasNext();) {
1243 fmd = (FieldMetaData) itr.next();
1244 sup = getSuperclassField(fmd);
1245
1246 // jpa metadata doesn't qualify superclass field names, so we
1247 // might not know the declaring type until now
1248 if (fmd.getDeclaringType() == Object.class) {
1249 fmd.setDeclaringType(sup.getDeclaringType());
1250 fmd.backingMember(getRepository().getMetaDataFactory().
1251 getDefaults().getBackingMember(fmd));
1252 }
1253 fmd.copy(sup);
1254 fmd.resolve(MODE_META);
1255 }
1256 }
1257
1258 /**
1259 * Returns an array of all the fields defined by this class.
1260 * This includes mapped declared fields and any concrete mapping of
1261 * unmapped superclass fields performed by this class.
1262 */
1263 public FieldMetaData[] getDefinedFields() {
1264 if (_definedFields == null) {
1265 FieldMetaData[] fields = getFields();
1266 List defined = new ArrayList(fields.length);
1267 for (int i = 0; i < fields.length; i++) {
1268 if (fields[i].isMapped()
1269 && fields[i].getDefiningMetaData() == this)
1270 defined.add(fields[i]);
1271 }
1272 _definedFields = (FieldMetaData[]) defined.toArray
1273 (_repos.newFieldMetaDataArray(defined.size()));
1274 }
1275 return _definedFields;
1276 }
1277
1278 /**
1279 * Returns all fields in the order they are listed in the metadata
1280 * file. Unlisted fields are placed after listed ones.
1281 */
1282 public FieldMetaData[] getFieldsInListingOrder() {
1283 if (_allListingFields == null) {
1284 // combine declared and unmanaged fields into listing order array
1285 FieldMetaData[] dec = getDeclaredFields();
1286 FieldMetaData[] unmgd = getDeclaredUnmanagedFields();
1287 FieldMetaData[] decListing = _repos.newFieldMetaDataArray
1288 (dec.length + unmgd.length);
1289 System.arraycopy(dec, 0, decListing, 0, dec.length);
1290 System.arraycopy(unmgd, 0, decListing, dec.length, unmgd.length);
1291 Arrays.sort(decListing, ListingOrderComparator.getInstance());
1292
1293 if (_super == null)
1294 _allListingFields = decListing;
1295 else {
1296 // place superclass fields in listing order before our
1297 // listing-order declared fields
1298 FieldMetaData[] sup = getPCSuperclassMetaData().
1299 getFieldsInListingOrder();
1300 FieldMetaData[] listing = _repos.newFieldMetaDataArray
1301 (sup.length + decListing.length);
1302 System.arraycopy(sup, 0, listing, 0, sup.length);
1303 replaceDefinedSuperclassFields(listing, sup.length);
1304 System.arraycopy(decListing, 0, listing, sup.length,
1305 decListing.length);
1306 _allListingFields = listing;
1307 }
1308 }
1309 return _allListingFields;
1310 }
1311
1312 /**
1313 * Returns all fields defined by this class in the order they are listed
1314 * in the metadata file. Unlisted fields are placed after listed ones.
1315 * This array includes declared transactional and unmanaged fields.
1316 */
1317 public FieldMetaData[] getDefinedFieldsInListingOrder() {
1318 if (_listingFields == null) {
1319 FieldMetaData[] fields = getFields();
1320 List defined = new ArrayList(fields.length);
1321 for (int i = 0; i < fields.length; i++)
1322 if (fields[i].getDefiningMetaData() == this)
1323 defined.add(fields[i]);
1324 FieldMetaData[] unmgd = getDeclaredUnmanagedFields();
1325 FieldMetaData[] listing = _repos.newFieldMetaDataArray
1326 (defined.size() + unmgd.length);
1327 for (int i = 0; i < defined.size(); i++)
1328 listing[i] = (FieldMetaData) defined.get(i);
1329 System.arraycopy(unmgd, 0, listing, defined.size(), unmgd.length);
1330 Arrays.sort(listing, ListingOrderComparator.getInstance());
1331 _listingFields = listing;
1332 }
1333 return _listingFields;
1334 }
1335
1336 /**
1337 * The name of the datacache to use for this class, or null if none.
1338 */
1339 public String getDataCacheName() {
1340 if (DEFAULT_STRING.equals(_cacheName)) {
1341 if (_super != null)
1342 _cacheName = getPCSuperclassMetaData().getDataCacheName();
1343 else
1344 _cacheName = DataCache.NAME_DEFAULT;
1345 }
1346 return _cacheName;
1347 }
1348
1349 /**
1350 * Set the cache name for this class. Set to null to disable caching.
1351 */
1352 public void setDataCacheName(String name) {
1353 _cacheName = name;
1354 }
1355
1356 /**
1357 * The cache timeout for this class. -1 indicates no timeout.
1358 */
1359 public int getDataCacheTimeout() {
1360 if (_cacheTimeout == Integer.MIN_VALUE) {
1361 if (_super != null)
1362 _cacheTimeout = getPCSuperclassMetaData().
1363 getDataCacheTimeout();
1364 else
1365 _cacheTimeout = _repos.getConfiguration().
1366 getDataCacheTimeout();
1367 }
1368 return _cacheTimeout;
1369 }
1370
1371 /**
1372 * The cache timeout for this class. -1 indicates no timeout.
1373 */
1374 public void setDataCacheTimeout(int timeout) {
1375 _cacheTimeout = timeout;
1376 }
1377
1378 /**
1379 * Return the data cache for this class, or null if it is not cachable.
1380 */
1381 public DataCache getDataCache() {
1382 String name = getDataCacheName();
1383 if (name == null)
1384 return null;
1385 return _repos.getConfiguration().getDataCacheManagerInstance().
1386 getDataCache(name, true);
1387 }
1388
1389 /**
1390 * Whether instances are detachable.
1391 */
1392 public boolean isDetachable() {
1393 if (_detachable == null) {
1394 if (_super != null)
1395 _detachable = (getPCSuperclassMetaData().isDetachable())
1396 ? Boolean.TRUE : Boolean.FALSE;
1397 else
1398 _detachable = Boolean.FALSE;
1399 }
1400 return _detachable.booleanValue();
1401 }
1402
1403 /**
1404 * Whether instances are detachable.
1405 */
1406 public void setDetachable(boolean detachable) {
1407 _detachable = (detachable) ? Boolean.TRUE : Boolean.FALSE;
1408 }
1409
1410 /**
1411 * The name of the detach state field, or null if none.
1412 */
1413 public String getDetachedState() {
1414 if (DEFAULT_STRING.equals(_detachState)) {
1415 ClassMetaData sup = getPCSuperclassMetaData();
1416 if (sup != null && sup.isDetachable() == isDetachable())
1417 _detachState = sup.getDetachedState();
1418 else {
1419 Boolean use = usesDetachedState(SYNTHETIC, true);
1420 _detachState = (Boolean.FALSE.equals(use)) ? null : SYNTHETIC;
1421 }
1422 }
1423 return _detachState;
1424 }
1425
1426 /**
1427 * The name of the detach state field, or null if none.
1428 */
1429 public void setDetachedState(String field) {
1430 _detachState = field;
1431 }
1432
1433 /**
1434 * Return the detach state field, or null if none.
1435 */
1436 public Field getDetachedStateField() {
1437 // no caching; only used at enhancement
1438 String fieldName = getDetachedState();
1439 if (fieldName == null || SYNTHETIC.equals(fieldName))
1440 return null;
1441
1442 Field f = Reflection.findField(_type, fieldName, false);
1443 if (f != null)
1444 return f;
1445 else
1446 throw new MetaDataException(
1447 _loc.get("no-detach-state", fieldName, _type));
1448 }
1449
1450 /**
1451 * Whether an instance of this type has detached state.
1452 *
1453 * @return true if a detached instance must have detached state, false
1454 * if it does not, and null if it may use a
1455 * manually-constructed instance without detached state
1456 */
1457 public Boolean usesDetachedState() {
1458 // no need to let conf disallow because it's taken into account in
1459 // getDetachedState() call
1460 return usesDetachedState(getDetachedState(), false);
1461 }
1462
1463 /**
1464 * Whether an instance of this type has detached state, assuming the given
1465 * detached state field.
1466 *
1467 * @return true if a detached instance must have detached state, false
1468 * if it does not, and null if it may use a
1469 * manually-constructed instance without detached state
1470 */
1471 private Boolean usesDetachedState(String detachedField,
1472 boolean confDisallows) {
1473 if (!isDetachable())
1474 return Boolean.FALSE;
1475
1476 // if we declare a detached state field, have to use it
1477 if (detachedField == null)
1478 return Boolean.FALSE;
1479 if (!SYNTHETIC.equals(detachedField))
1480 return Boolean.TRUE;
1481
1482 // allow conf to disallow
1483 if (confDisallows && !_repos.getConfiguration().
1484 getDetachStateInstance().getDetachedStateField())
1485 return Boolean.FALSE;
1486
1487 // have to use detached state to store datastore id
1488 if (getIdentityType() == ID_DATASTORE)
1489 return Boolean.TRUE;
1490
1491 // allow detached state use, but don't require
1492 return null;
1493 }
1494
1495 /**
1496 * Clear cached field data.
1497 */
1498 protected void clearAllFieldCache() {
1499 _allFields = null;
1500 _allDFGFields = null;
1501 _allPKFields = null;
1502 _definedFields = null;
1503 _listingFields = null;
1504 _allListingFields = null;
1505 _fieldDataTable = null;
1506 }
1507
1508 /**
1509 * Clear defined field data.
1510 */
1511 protected void clearDefinedFieldCache() {
1512 _definedFields = null;
1513 _listingFields = null;
1514 }
1515
1516 /**
1517 * Clear cached field data.
1518 */
1519 protected void clearFieldCache() {
1520 clearAllFieldCache();
1521 _fields = null;
1522 _unmgdFields = null;
1523 _versionIdx = Integer.MIN_VALUE;
1524 }
1525
1526 /**
1527 * Clear cached subclass data.
1528 */
1529 protected void clearSubclassCache() {
1530 _subs = null;
1531 _subMetas = null;
1532 _mapSubMetas = null;
1533 }
1534
1535 /**
1536 * Clear impl data and intermediate data table.
1537 */
1538 void clearExtraFieldDataTable() {
1539 _fieldDataTable = null;
1540 }
1541
1542 /**
1543 * Cache field arrays.
1544 */
1545 private void cacheFields() {
1546 getFields();
1547 getPrimaryKeyFields();
1548 }
1549
1550 public int hashCode() {
1551 return _type.getName().hashCode();
1552 }
1553
1554 public boolean equals(Object other) {
1555 if (other == this)
1556 return true;
1557 if (!(other instanceof ClassMetaData))
1558 return false;
1559 return _type == ((ClassMetaData) other).getDescribedType();
1560 }
1561
1562 public int compareTo(Object other) {
1563 if (other == this)
1564 return 0;
1565 return _type.getName().compareTo(((ClassMetaData) other).
1566 getDescribedType().getName());
1567 }
1568
1569 public String toString() {
1570 return getDescribedType().getName();
1571 }
1572
1573 ////////////////////////
1574 // Resolve and validate
1575 ////////////////////////
1576
1577 /**
1578 * The resolve mode for this metadata.
1579 */
1580 public int getResolve() {
1581 return _resMode;
1582 }
1583
1584 /**
1585 * The resolve mode for this metadata.
1586 */
1587 public void setResolve(int mode) {
1588 _resMode = mode;
1589 }
1590
1591 /**
1592 * The resolve mode for this metadata.
1593 */
1594 public void setResolve(int mode, boolean on) {
1595 if (mode == MODE_NONE)
1596 _resMode = mode;
1597 else if (on)
1598 _resMode |= mode;
1599 else
1600 _resMode &= ~mode;
1601 }
1602
1603 /**
1604 * Resolve and validate metadata. Return true if already resolved.
1605 */
1606 public boolean resolve(int mode) {
1607 if ((_resMode & mode) == mode)
1608 return true;
1609 int cur = _resMode;
1610 _resMode |= mode;
1611
1612 int val = _repos.getValidate();
1613 boolean runtime = (val & _repos.VALIDATE_RUNTIME) != 0;
1614 boolean validate =
1615 !ImplHelper.isManagedType(getRepository().getConfiguration(), _type)
1616 || (val & MetaDataRepository.VALIDATE_UNENHANCED) == 0;
1617
1618 // we only do any actions for metadata mode
1619 if ((mode & MODE_META) != 0 && (cur & MODE_META) == 0) {
1620 resolveMeta(runtime);
1621 if (validate && (val & _repos.VALIDATE_META) != 0)
1622 validateMeta(runtime);
1623 }
1624 if ((mode & MODE_MAPPING) != 0 && (cur & MODE_MAPPING) == 0) {
1625 resolveMapping(runtime);
1626 if (validate && (val & _repos.VALIDATE_MAPPING) != 0)
1627 validateMapping(runtime);
1628 }
1629 if ((mode & MODE_MAPPING_INIT) != 0 && (cur & MODE_MAPPING_INIT) == 0)
1630 initializeMapping();
1631 return false;
1632 }
1633
1634 /**
1635 * Resolve metadata.
1636 */
1637 protected void resolveMeta(boolean runtime) {
1638 boolean embed = _owner != null && _owner.getDeclaredType() == _type;
1639 Log log = _repos.getLog();
1640 if (log.isTraceEnabled())
1641 log.trace(_loc.get((embed) ? "resolve-embed-meta" : "resolve-meta",
1642 this + "@" + System.identityHashCode(this)));
1643
1644 if (runtime && !_type.isInterface() &&
1645 !ImplHelper.isManagedType(getRepository().getConfiguration(),_type))
1646 throw new MetaDataException(_loc.get("not-enhanced", _type));
1647
1648 // are we the target of an embedded value?
1649 if (embed) {
1650 if (_owner.getFieldMetaData().getDefiningMetaData().
1651 getDescribedType().isAssignableFrom(_type))
1652 throw new MetaDataException(_loc.get("recurse-embed", _owner));
1653
1654 // copy info from the "real" metadata for this type
1655 ClassMetaData meta = _repos.getMetaData(_type, _loader, true);
1656 meta.resolve(MODE_META);
1657 copy(this, meta);
1658 _embedded = Boolean.FALSE; // embedded instance isn't embedded-only
1659 }
1660
1661 // make sure superclass is resolved
1662 ClassMetaData sup = getPCSuperclassMetaData();
1663 if (sup != null) {
1664 sup.resolve(MODE_META);
1665 if (embed) {
1666 // embedded instance always redefine all superclass fields
1667 FieldMetaData[] sups = sup.getFields();
1668 for (int i = 0; i < sups.length; i++) {
1669 if (_supFieldMap == null
1670 || !_supFieldMap.containsKey(sups[i].getName())) {
1671 addDefinedSuperclassField(sups[i].getName(),
1672 sups[i].getDeclaredType(),
1673 sups[i].getDeclaringType());
1674 }
1675 }
1676 }
1677 }
1678
1679 // resolve fields and remove invalids
1680 FieldMetaData fmd;
1681 for (Iterator itr = _fieldMap.values().iterator(); itr.hasNext();) {
1682 // only pass on metadata resolve mode so that metadata is always
1683 // resolved before any other resolve modes our subclasses pass along
1684 fmd = (FieldMetaData) itr.next();
1685 fmd.resolve(MODE_META);
1686
1687 if (!fmd.isExplicit()
1688 && (fmd.getDeclaredTypeCode() == JavaTypes.OBJECT
1689 || fmd.getDeclaredTypeCode() == JavaTypes.PC_UNTYPED
1690 || (fmd.getDeclaredTypeCode() == JavaTypes.ARRAY
1691 && fmd.getElement().getDeclaredTypeCode()
1692 == JavaTypes.OBJECT))) {
1693 _repos.getLog().warn(_loc.get("rm-field", fmd));
1694 if (fmd.getListingIndex() != -1)
1695 fmd.setManagement(fmd.MANAGE_NONE);
1696 else
1697 itr.remove();
1698 clearFieldCache();
1699 }
1700 }
1701
1702 // embedded instances must embed all superclass fields too
1703 if (embed) {
1704 clearAllFieldCache();
1705 resolveDefinedSuperclassFields();
1706 }
1707
1708 // this ensures that all field indexes get set when fields are cached
1709 cacheFields();
1710
1711 // resolve lifecycle metadata now to prevent lazy threading problems
1712 _lifeMeta.resolve();
1713
1714 // record implements in the repository
1715 if (_interfaces != null) {
1716 for (Iterator it = _interfaces.iterator(); it.hasNext();)
1717 _repos.addDeclaredInterfaceImpl(this, (Class) it.next());
1718 }
1719
1720 // resolve fetch groups
1721 if (_fgMap != null)
1722 for (Iterator itr = _fgMap.values().iterator(); itr.hasNext();)
1723 ((FetchGroup) itr.next()).resolve();
1724
1725 if (!embed && _type.isInterface()) {
1726 if (_interface != Boolean.TRUE)
1727 throw new MetaDataException(_loc.get("interface", _type));
1728
1729 if (runtime) {
1730 _impl = _repos.getImplGenerator().createImpl(this);
1731 _repos.setInterfaceImpl(this, _impl);
1732 }
1733 }
1734
1735 // if this is runtime, create a pc instance and scan it for comparators
1736 if (runtime && !Modifier.isAbstract(_type.getModifiers())) {
1737 ProxySetupStateManager sm = new ProxySetupStateManager();
1738 sm.setProxyData(PCRegistry.newInstance(_type, sm, false), this);
1739 }
1740 }
1741
1742 /**
1743 * Validate resolved metadata.
1744 */
1745 protected void validateMeta(boolean runtime) {
1746 validateDataCache();
1747 validateDetachable();
1748 validateExtensionKeys();
1749 validateIdentity();
1750 validateAccessType();
1751 }
1752
1753 /**
1754 * Resolve mapping data. Logs resolve message and resolves super by default.
1755 */
1756 protected void resolveMapping(boolean runtime) {
1757 Log log = _repos.getLog();
1758 if (log.isTraceEnabled())
1759 log.trace(_loc.get("resolve-mapping", this + "@"
1760 + System.identityHashCode(this)));
1761
1762 // make sure superclass is resolved first
1763 ClassMetaData sup = getPCSuperclassMetaData();
1764 if (sup != null)
1765 sup.resolve(MODE_MAPPING);
1766 }
1767
1768 /**
1769 * Validate mapping data.
1770 */
1771 protected void validateMapping(boolean runtime) {
1772 }
1773
1774 /**
1775 * Initialize mapping. Logs init message by default.
1776 */
1777 protected void initializeMapping() {
1778 Log log = _repos.getLog();
1779 if (log.isTraceEnabled())
1780 log.trace(_loc.get("init-mapping", this + "@"
1781 + System.identityHashCode(this)));
1782 }
1783
1784 /**
1785 * Validate data cache settings.
1786 */
1787 private void validateDataCache() {
1788 int timeout = getDataCacheTimeout();
1789 if (timeout < -1 || timeout == 0)
1790 throw new MetaDataException(_loc.get("cache-timeout-invalid",
1791 _type, String.valueOf(timeout)));
1792
1793 if (_super == null)
1794 return;
1795 String cache = getDataCacheName();
1796 if (cache == null)
1797 return;
1798
1799 String superCache = getPCSuperclassMetaData().getDataCacheName();
1800 if (!StringUtils.equals(cache, superCache))
1801 throw new MetaDataException(_loc.get("cache-names", new Object[]
1802 { _type, cache, _super, superCache }));
1803 }
1804
1805 /**
1806 * Assert that the identity handling for this class is valid.
1807 */
1808 private void validateIdentity() {
1809 // make sure identity types are consistent
1810 ClassMetaData sup = getPCSuperclassMetaData();
1811 int id = getIdentityType();
1812 if (sup != null && sup.getIdentityType() != ID_UNKNOWN
1813 && sup.getIdentityType() != id)
1814 throw new MetaDataException(_loc.get("id-types", _type));
1815
1816 // check for things the data store doesn't support
1817 Collection opts = _repos.getConfiguration().supportedOptions();
1818 if (id == ID_APPLICATION
1819 && !opts.contains(OpenJPAConfiguration.OPTION_ID_APPLICATION)) {
1820 throw new UnsupportedException(_loc.get("appid-not-supported",
1821 _type));
1822 }
1823 if (id == ID_DATASTORE
1824 && !opts.contains(OpenJPAConfiguration.OPTION_ID_DATASTORE)) {
1825 throw new UnsupportedException(_loc.get
1826 ("datastoreid-not-supported", _type));
1827 }
1828
1829 if (id == ID_APPLICATION) {
1830 if (_idStrategy != ValueStrategies.NONE)
1831 throw new MetaDataException(_loc.get("appid-strategy", _type));
1832 validateAppIdClass();
1833 } else if (id != ID_UNKNOWN)
1834 validateNoPKFields();
1835
1836 int strategy = getIdentityStrategy();
1837 if (strategy == ValueStrategies.SEQUENCE
1838 && getIdentitySequenceName() == null)
1839 throw new MetaDataException(_loc.get("no-seq-name", _type));
1840
1841 ValueStrategies.assertSupported(strategy, this,
1842 "datastore identity strategy");
1843 }
1844
1845 /**
1846 * Make sure the application identity class is valid.
1847 */
1848 private void validateAppIdClass() {
1849 // base types must declare an oid class if not single-field identity
1850 FieldMetaData[] pks = getPrimaryKeyFields();
1851 if (getObjectIdType() == null) {
1852 if (pks.length == 1)
1853 throw new MetaDataException(_loc.get("unsupported-id-type",
1854 _type, pks[0].getName(),
1855 pks[0].getDeclaredType().getName()));
1856 throw new MetaDataException(_loc.get("no-id-class", _type));
1857 }
1858 if (_objectId == null)
1859 return;
1860
1861 if (isOpenJPAIdentity()) {
1862 if (pks[0].getDeclaredTypeCode() == JavaTypes.OID) {
1863 ClassMetaData embed = pks[0].getEmbeddedMetaData();
1864 validateAppIdClassMethods(embed.getDescribedType());
1865 validateAppIdClassPKs(embed, embed.getFields(),
1866 embed.getDescribedType());
1867 }
1868 return;
1869 }
1870
1871 if (_super != null) {
1872 // concrete superclass oids must match or be parent of ours
1873 ClassMetaData sup = getPCSuperclassMetaData();
1874 if (!sup.getObjectIdType().isAssignableFrom(_objectId))
1875 throw new MetaDataException(_loc.get("id-classes",
1876 new Object[]{ _type, _objectId, _super,
1877 sup.getObjectIdType() }));
1878
1879 // validate that no other pks are declared if we have a
1880 // concrete PC superclass
1881 if (hasConcretePCSuperclass())
1882 validateNoPKFields();
1883 }
1884
1885 // if this class has its own oid class, do some more validation
1886 if (_super == null
1887 || _objectId != getPCSuperclassMetaData().getObjectIdType()) {
1888 // make sure non-abstract oid classes override the proper methods
1889 if (!Modifier.isAbstract(_objectId.getModifiers()))
1890 validateAppIdClassMethods(_objectId);
1891
1892 // make sure the app id class has all pk fields
1893 validateAppIdClassPKs(this, pks, _objectId);
1894 }
1895 }
1896
1897 /**
1898 * Return true if this class has a concrete persistent superclass.
1899 */
1900 private boolean hasConcretePCSuperclass() {
1901 if (_super == null)
1902 return false;
1903 if (!Modifier.isAbstract(_super.getModifiers()))
1904 return true;
1905 return getPCSuperclassMetaData().hasConcretePCSuperclass();
1906 }
1907
1908 /**
1909 * Ensure that the user has overridden the equals and hashCode methods,
1910 * and has the proper constructors.
1911 */
1912 private void validateAppIdClassMethods(Class oid) {
1913 try {
1914 oid.getConstructor((Class[]) null);
1915 } catch (Exception e) {
1916 throw new MetaDataException(_loc.get("null-cons", _type)).
1917 setCause(e);
1918 }
1919
1920 // check for equals and hashcode overrides; don't enforce it
1921 // for abstract app id classes, since they may not necessarily
1922 // declare primary key fields
1923 Method method;
1924 try {
1925 method = oid.getMethod("equals", new Class[]{ Object.class });
1926 } catch (Exception e) {
1927 throw new GeneralException(e).setFatal(true);
1928 }
1929
1930 boolean abs = Modifier.isAbstract(_type.getModifiers());
1931 if (!abs && method.getDeclaringClass() == Object.class)
1932 throw new MetaDataException(_loc.get("eq-method", _type));
1933
1934 try {
1935 method = oid.getMethod("hashCode", (Class[]) null);
1936 } catch (Exception e) {
1937 throw new GeneralException(e).setFatal(true);
1938 }
1939 if (!abs && method.getDeclaringClass() == Object.class)
1940 throw new MetaDataException(_loc.get("hc-method", _type));
1941 }
1942
1943 /**
1944 * Validate that the primary key class has all pk fields.
1945 */
1946 private void validateAppIdClassPKs(ClassMetaData meta,
1947 FieldMetaData[] fmds, Class oid) {
1948 if (fmds.length == 0 && !Modifier.isAbstract(meta.getDescribedType().
1949 getModifiers()))
1950 throw new MetaDataException(_loc.get("no-pk", _type));
1951
1952 // check that the oid type contains all pk fields
1953 Field f;
1954 Method m;
1955 Class c;
1956 for (int i = 0; i < fmds.length; i++) {
1957 switch (fmds[i].getDeclaredTypeCode()) {
1958 case JavaTypes.ARRAY:
1959 c = fmds[i].getDeclaredType().getComponentType();
1960 if (c == byte.class || c == Byte.class
1961 || c == char.class || c == Character.class) {
1962 c = fmds[i].getDeclaredType();
1963 break;
1964 }
1965 // else no break
1966 case JavaTypes.PC_UNTYPED:
1967 case JavaTypes.COLLECTION:
1968 case JavaTypes.MAP:
1969 case JavaTypes.OID: // we're validating embedded fields
1970 throw new MetaDataException(_loc.get("bad-pk-type",
1971 fmds[i]));
1972 default:
1973 c = fmds[i].getObjectIdFieldType();
1974 }
1975
1976 if (meta.getAccessType() == ACCESS_FIELD) {
1977 f = Reflection.findField(oid, fmds[i].getName(), false);
1978 if (f == null || !f.getType().isAssignableFrom(c))
1979 throw new MetaDataException(_loc.get("invalid-id",
1980 _type, fmds[i].getName()));
1981 } else if (meta.getAccessType() == ACCESS_PROPERTY) {
1982 m = Reflection.findGetter(oid, fmds[i].getName(), false);
1983 if (m == null || !m.getReturnType().isAssignableFrom(c))
1984 throw new MetaDataException(_loc.get("invalid-id",
1985 _type, fmds[i].getName()));
1986 m = Reflection.findSetter(oid, fmds[i].getName(),
1987 fmds[i].getObjectIdFieldType(), false);
1988 if (m == null || m.getReturnType() != void.class)
1989 throw new MetaDataException(_loc.get("invalid-id",
1990 _type, fmds[i].getName()));
1991 }
1992 }
1993 }
1994
1995 /**
1996 * Validate that this class doesn't declare any primary key fields.
1997 */
1998 private void validateNoPKFields() {
1999 FieldMetaData[] fields = getDeclaredFields();
2000 for (int i = 0; i < fields.length; i++)
2001 if (fields[i].isPrimaryKey())
2002 throw new MetaDataException(_loc.get("bad-pk", fields[i]));
2003 }
2004
2005 /**
2006 * Assert that this class' access type is allowed.
2007 */
2008 private void validateAccessType() {
2009 if (_accessType == ACCESS_UNKNOWN)
2010 return;
2011 ClassMetaData sup = getPCSuperclassMetaData();
2012 if (sup != null && sup.getAccessType() != ACCESS_UNKNOWN
2013 && sup.getAccessType() != _accessType &&
2014 getPCSuperclassMetaData().getFields().length > 0) {
2015 throw new MetaDataException(_loc.get("inconsistent-access",
2016 this, sup));
2017 }
2018 }
2019
2020 /**
2021 * Assert that detachment configuration is valid.
2022 */
2023 private void validateDetachable() {
2024 boolean first = true;
2025 for (ClassMetaData parent = getPCSuperclassMetaData();
2026 first && parent != null; parent = parent.getPCSuperclassMetaData())
2027 {
2028 if (parent.isDetachable())
2029 first = false;
2030 }
2031
2032 Field field = getDetachedStateField();
2033 if (field != null) {
2034 if (!first)
2035 throw new MetaDataException(_loc.get("parent-detach-state",
2036 _type));
2037 if (getField(field.getName()) != null)
2038 throw new MetaDataException(_loc.get("managed-detach-state",
2039 field.getName(), _type));
2040 if (field.getType() != Object.class)
2041 throw new MetaDataException(_loc.get("bad-detach-state",
2042 field.getName(), _type));
2043 }
2044 }
2045
2046 ///////////////
2047 // Fetch Group
2048 ///////////////
2049
2050 /**
2051 * Return the fetch groups declared explicitly in this type.
2052 */
2053 public FetchGroup[] getDeclaredFetchGroups() {
2054 if (_fgs == null)
2055 _fgs = (_fgMap == null) ? EMPTY_FETCH_GROUP_ARRAY : (FetchGroup[])
2056 _fgMap.values().toArray(new FetchGroup[_fgMap.size()]);
2057 return _fgs;
2058 }
2059
2060 /**
2061 * Return all fetch groups for this type, including superclass groups.
2062 */
2063 public FetchGroup[] getCustomFetchGroups() {
2064 if (_customFGs == null) {
2065 // map fgs to names, allowing our fgs to override supers
2066 Map fgs = new HashMap();
2067 ClassMetaData sup = getPCSuperclassMetaData();
2068 if (sup != null)
2069 {
2070 FetchGroup[] supFGs = sup.getCustomFetchGroups();
2071 for (int i = 0; i < supFGs.length; i++)
2072 fgs.put(supFGs[i].getName(), supFGs[i]);
2073 }
2074 FetchGroup[] decs = getDeclaredFetchGroups();
2075 for (int i = 0; i < decs.length; i++)
2076 fgs.put(decs[i].getName(), decs[i]);
2077
2078 // remove std groups
2079 fgs.remove(FetchGroup.NAME_DEFAULT);
2080 fgs.remove(FetchGroup.NAME_ALL);
2081
2082 _customFGs = (FetchGroup[]) fgs.values().toArray
2083 (new FetchGroup[fgs.size()]);
2084 }
2085 return _customFGs;
2086 }
2087
2088 /**
2089 * Gets a named fecth group. If not available in this receiver then looks
2090 * up the inheritence hierarchy.
2091 *
2092 * @param name name of a fetch group.
2093 * @return an existing fecth group of the given name if known to this
2094 * receiver or any of its superclasses. Otherwise null.
2095 */
2096 public FetchGroup getFetchGroup(String name) {
2097 FetchGroup fg = (_fgMap == null) ? null : (FetchGroup) _fgMap.get(name);
2098 if (fg != null)
2099 return fg;
2100 ClassMetaData sup = getPCSuperclassMetaData();
2101 if (sup != null)
2102 return sup.getFetchGroup(name);
2103 if (FetchGroup.NAME_DEFAULT.equals(name))
2104 return FetchGroup.DEFAULT;
2105 if (FetchGroup.NAME_ALL.equals(name))
2106 return FetchGroup.ALL;
2107 return null;
2108 }
2109
2110 /**
2111 * Adds fetch group of the given name, or returns existing instance.
2112 *
2113 * @param name a non-null, non-empty name. Must be unique within this
2114 * receiver's scope. The super class <em>may</em> have a group with
2115 * the same name.
2116 */
2117 public FetchGroup addDeclaredFetchGroup(String name) {
2118 if (StringUtils.isEmpty(name))
2119 throw new MetaDataException(_loc.get("empty-fg-name", this));
2120 if (_fgMap == null)
2121 _fgMap = new HashMap();
2122 FetchGroup fg = (FetchGroup) _fgMap.get(name);
2123 if (fg == null) {
2124 fg = new FetchGroup(this, name);
2125 _fgMap.put(name, fg);
2126 _fgs = null;
2127 _customFGs = null;
2128 }
2129 return fg;
2130 }
2131
2132 /**
2133 * Remove a declared fetch group.
2134 */
2135 public boolean removeDeclaredFetchGroup(FetchGroup fg) {
2136 if (fg == null)
2137 return false;
2138 if (_fgMap.remove(fg.getName()) != null) {
2139 _fgs = null;
2140 _customFGs = null;
2141 return true;
2142 }
2143 return false;
2144 }
2145
2146 /////////////////
2147 // SourceTracker
2148 /////////////////
2149
2150 public File getSourceFile() {
2151 return _srcFile;
2152 }
2153
2154 public Object getSourceScope() {
2155 return null;
2156 }
2157
2158 public int getSourceType() {
2159 return _srcType;
2160 }
2161
2162 public void setSource(File file, int srcType) {
2163 _srcFile = file;
2164 _srcType = srcType;
2165 }
2166
2167 public String getResourceName() {
2168 return _type.getName();
2169 }
2170
2171 /**
2172 * The source mode this metadata has been loaded under.
2173 */
2174 public int getSourceMode() {
2175 return _srcMode;
2176 }
2177
2178 /**
2179 * The source mode this metadata has been loaded under.
2180 */
2181 public void setSourceMode(int mode) {
2182 _srcMode = mode;
2183 }
2184
2185 /**
2186 * The source mode this metadata has been loaded under.
2187 */
2188 public void setSourceMode(int mode, boolean on) {
2189 if (mode == MODE_NONE)
2190 _srcMode = mode;
2191 else if (on)
2192 _srcMode |= mode;
2193 else
2194 _srcMode &= ~mode;
2195 }
2196
2197 /**
2198 * The index in which this class was listed in the metadata. Defaults to
2199 * <code>-1</code> if this class was not listed in the metadata.
2200 */
2201 public int getListingIndex() {
2202 return _listIndex;
2203 }
2204
2205 /**
2206 * The index in which this field was listed in the metadata. Defaults to
2207 * <code>-1</code> if this class was not listed in the metadata.
2208 */
2209 public void setListingIndex(int index) {
2210 _listIndex = index;
2211 }
2212
2213 ///////////////
2214 // Commentable
2215 ///////////////
2216
2217 public String[] getComments() {
2218 return (_comments == null) ? EMPTY_COMMENTS : _comments;
2219 }
2220
2221 public void setComments(String[] comments) {
2222 _comments = comments;
2223 }
2224
2225 //////////////
2226 // State copy
2227 //////////////
2228
2229 /**
2230 * Copy the metadata from the given instance to this one. Do not
2231 * copy mapping information.
2232 */
2233 public void copy(ClassMetaData meta) {
2234 if (meta.getDescribedType() != _type)
2235 throw new InternalException();
2236 super.copy(meta);
2237
2238 // copy class-level info; use get methods to force resolution of
2239 // lazy data
2240 _super = meta.getPCSuperclass();
2241 _objectId = meta.getObjectIdType();
2242 _extent = (meta.getRequiresExtent()) ? Boolean.TRUE : Boolean.FALSE;
2243 _embedded = (meta.isEmbeddedOnly()) ? Boolean.TRUE : Boolean.FALSE;
2244 _interface = (meta.isManagedInterface()) ? Boolean.TRUE : Boolean.FALSE;
2245 setIntercepting(meta.isIntercepting());
2246 _impl = meta.getInterfaceImpl();
2247 _identity = meta.getIdentityType();
2248 _idStrategy = meta.getIdentityStrategy();
2249 _seqName = meta.getIdentitySequenceName();
2250 _seqMeta = null;
2251 _alias = meta.getTypeAlias();
2252 _accessType = meta.getAccessType();
2253
2254 // only copy this information if it wasn't set explicitly for this
2255 // instance
2256 if (DEFAULT_STRING.equals(_cacheName))
2257 _cacheName = meta.getDataCacheName();
2258 if (_cacheTimeout == Integer.MIN_VALUE)
2259 _cacheTimeout = meta.getDataCacheTimeout();
2260 if (_detachable == null)
2261 _detachable = meta._detachable;
2262 if (DEFAULT_STRING.equals(_detachState))
2263 _detachState = meta.getDetachedState();
2264
2265 // synch field information; first remove extra fields
2266 clearFieldCache();
2267 _fieldMap.keySet().retainAll(meta._fieldMap.keySet());
2268
2269 // add copies of declared fields; other defined fields already copied
2270 FieldMetaData[] fields = meta.getDeclaredFields();
2271 FieldMetaData field;
2272 for (int i = 0; i < fields.length; i++) {
2273 field = getDeclaredField(fields[i].getName());
2274 if (field == null)
2275 field = addDeclaredField(fields[i].getName(),
2276 fields[i].getDeclaredType());
2277 field.setDeclaredIndex(-1);
2278 field.setIndex(-1);
2279 field.copy(fields[i]);
2280 }
2281
2282 // copy fetch groups
2283 FetchGroup[] fgs = meta.getDeclaredFetchGroups();
2284 FetchGroup fg;
2285 for (int i = 0; i < fgs.length; i++) {
2286 fg = addDeclaredFetchGroup(fgs[i].getName());
2287 fg.copy(fgs[i]);
2288 }
2289
2290 // copy iface re-mapping
2291 _ifaceMap.clear();
2292 _ifaceMap.putAll(meta._ifaceMap);
2293 }
2294
2295 /**
2296 * Recursive helper to copy embedded metadata.
2297 */
2298 private static void copy(ClassMetaData embed, ClassMetaData dec) {
2299 ClassMetaData sup = dec.getPCSuperclassMetaData();
2300 if (sup != null) {
2301 embed.setPCSuperclass(sup.getDescribedType());
2302 copy(embed.getPCSuperclassMetaData(), sup);
2303 }
2304 embed.copy(dec);
2305 }
2306
2307 protected void addExtensionKeys(Collection exts) {
2308 _repos.getMetaDataFactory().addClassExtensionKeys(exts);
2309 }
2310
2311 /**
2312 * Comparator used to put field metadatas into listing order.
2313 */
2314 private static class ListingOrderComparator
2315 implements Comparator {
2316
2317 private static final ListingOrderComparator _instance
2318 = new ListingOrderComparator();
2319
2320 /**
2321 * Access singleton instance.
2322 */
2323 public static ListingOrderComparator getInstance() {
2324 return _instance;
2325 }
2326
2327 public int compare(Object o1, Object o2) {
2328 if (o1 == o2)
2329 return 0;
2330 if (o1 == null)
2331 return 1;
2332 if (o2 == null)
2333 return -1;
2334
2335 FieldMetaData f1 = (FieldMetaData) o1;
2336 FieldMetaData f2 = (FieldMetaData) o2;
2337 if (f1.getListingIndex() == f2.getListingIndex()) {
2338 if (f1.getIndex() == f2.getIndex())
2339 return f1.getFullName(false).compareTo
2340 (f2.getFullName(false));
2341 if (f1.getIndex () == -1)
2342 return 1;
2343 if (f2.getIndex () == -1)
2344 return -1;
2345 return f1.getIndex () - f2.getIndex ();
2346 }
2347 if (f1.getListingIndex () == -1)
2348 return 1;
2349 if (f2.getListingIndex () == -1)
2350 return -1;
2351 return f1.getListingIndex () - f2.getListingIndex ();
2352 }
2353 }
2354 }