001: /**********************************************************************
002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2007 Xuan Baldauf - little reduction in code duplication to anticipate changes regarding issue http://www.jpox.org/servlet/jira/browse/CORE-3272
018: ...
019: **********************************************************************/package org.jpox.metadata;
020:
021: import java.lang.reflect.Method;
022: import java.lang.reflect.Modifier;
023: import java.util.Collections;
024: import java.util.HashMap;
025: import java.util.Iterator;
026:
027: import org.jpox.ClassLoaderResolver;
028: import org.jpox.exceptions.ClassNotResolvedException;
029: import org.jpox.exceptions.JPOXException;
030: import org.jpox.exceptions.JPOXUserException;
031: import org.jpox.util.ClassUtils;
032: import org.jpox.util.JPOXLogger;
033:
034: /**
035: * Representation of the MetaData of a "persistent-interface".
036: *
037: * @since 1.1
038: * @version $Revision: 1.44 $
039: */
040: public class InterfaceMetaData extends AbstractClassMetaData {
041: // -------------------------------------------------------------------------
042: // Fields below here are not represented in the output MetaData. They are
043: // for use internally in the operation of the JDO system. The majority are
044: // for convenience to save iterating through the fields since the fields
045: // are fixed once initialised.
046:
047: /** whether the populate method is running **/
048: private boolean populating = false;
049:
050: // ----------------------------- Constructors ------------------------------
051:
052: /**
053: * Constructor.
054: * Takes the basic string information found in the MetaData file.
055: * @param parent The package to which this class belongs
056: * @param name Name of class
057: * @param identityType Type of identity
058: * @param objectidClass Class of the object id
059: * @param requiresExtent Whether the class requires an extent
060: * @param detachable Whether this is detachable
061: * @param embeddedOnly embedded-only tag
062: * @param catalog Name for catalog
063: * @param schema Name for schema
064: * @param table Name of the table where to persist objects of this type
065: * @param entityName the entity name required by JPA §4.3.1
066: */
067: public InterfaceMetaData(final PackageMetaData parent,
068: final String name, final String identityType,
069: final String objectidClass, final String requiresExtent,
070: final String detachable, final String embeddedOnly,
071: final String catalog, final String schema,
072: final String table, final String entityName) {
073: super (
074: parent,
075: name,
076: identityType,
077: objectidClass,
078: requiresExtent,
079: detachable,
080: embeddedOnly,
081: ClassPersistenceModifier.PERSISTENCE_CAPABLE.toString(),
082: null, catalog, schema, table, entityName);
083: }
084:
085: /**
086: * Method to initialise the object, creating internal convenience arrays.
087: * Initialises all sub-objects. If populate() is going to be used it should
088: * be used BEFORE calling this method.
089: */
090: public void initialise() {
091: if (populating) {
092: return;
093: }
094: checkPopulated();
095: if (isInitialised()) {
096: return;
097: }
098:
099: if (pcSuperclassMetaData != null) {
100: // We need our superclass to be initialised before us because we rely on information there
101: if (!pcSuperclassMetaData.isInitialised()) {
102: pcSuperclassMetaData.initialise();
103: }
104: }
105:
106: if (JPOXLogger.METADATA.isDebugEnabled()) {
107: JPOXLogger.METADATA
108: .debug(LOCALISER.msg("044076", fullName));
109: }
110:
111: // Count the fields of the relevant category
112: Iterator fields_iter = members.iterator();
113: int no_of_managed_fields = 0;
114: int no_of_overridden_fields = 0;
115: while (fields_iter.hasNext()) {
116: AbstractMemberMetaData fmd = (AbstractMemberMetaData) fields_iter
117: .next();
118:
119: // Initialise the AbstractMemberMetaData (and its sub-objects)
120: fmd.initialise();
121: if (fmd.isJdoField()) {
122: if (fmd.fieldBelongsToClass()) {
123: no_of_managed_fields++;
124: } else {
125: no_of_overridden_fields++;
126: }
127: }
128: }
129:
130: // Generate the "managed fields" list
131: managedMembers = new AbstractMemberMetaData[no_of_managed_fields];
132: overriddenMembers = new AbstractMemberMetaData[no_of_overridden_fields];
133:
134: fields_iter = members.iterator();
135: int field_id = 0;
136: int overridden_field_id = 0;
137: memberPositionsByName = new HashMap();
138: while (fields_iter.hasNext()) {
139: AbstractMemberMetaData fmd = (AbstractMemberMetaData) fields_iter
140: .next();
141:
142: if (fmd.isJdoField()) {
143: if (fmd.fieldBelongsToClass()) {
144: fmd.setFieldId(field_id);
145: managedMembers[field_id] = fmd;
146: memberPositionsByName.put(fmd.getName(),
147: new Integer(field_id));
148: field_id++;
149: } else {
150: overriddenMembers[overridden_field_id++] = fmd;
151: }
152: }
153: }
154:
155: if (pcSuperclassMetaData != null) {
156: if (!pcSuperclassMetaData.isInitialised()) {
157: pcSuperclassMetaData.initialise();
158: }
159: noOfInheritedManagedMembers = pcSuperclassMetaData
160: .getNoOfInheritedManagedMembers()
161: + pcSuperclassMetaData.getNoOfManagedMembers();
162: }
163:
164: // Set up the various convenience arrays of field numbers
165: int total_field_count = noOfInheritedManagedMembers
166: + managedMembers.length;
167: this .memberCount = total_field_count;
168: allMemberPositions = new int[total_field_count];
169: dfgMemberFlags = new boolean[total_field_count];
170: scoMutableMemberFlags = new boolean[total_field_count];
171: nonPkMemberFlags = new boolean[total_field_count];
172: int pk_field_count = 0;
173: int dfg_field_count = 0;
174: int scm_field_count = 0;
175: int pc_field_count = 0;
176: for (int i = 0; i < total_field_count; i++) {
177: allMemberPositions[i] = i;
178:
179: AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
180: if (fmd.isPrimaryKey()) {
181: pk_field_count++;
182: } else {
183: nonPkMemberFlags[i] = true;
184: }
185: if (fmd.isDefaultFetchGroup()) {
186: dfgMemberFlags[i] = true;
187: dfg_field_count++;
188: }
189: if (fmd.calcIsSecondClassMutable()) {
190: scoMutableMemberFlags[i] = true;
191: scm_field_count++;
192: }
193: if (fmd.isFieldTypePersistable()) {
194: pc_field_count++;
195: }
196: }
197:
198: if (pk_field_count > 0
199: && identityType != IdentityType.APPLICATION) {
200: // primary key fields found, but not using application identity
201: throw new InvalidMetaDataException(LOCALISER, "044078",
202: fullName, new Integer(pk_field_count), identityType);
203: } else if (pk_field_count > 0) {
204: pkMemberPositions = new int[pk_field_count];
205: for (int i = 0, pk_num = 0; i < total_field_count; i++) {
206: AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
207: if (fmd.isPrimaryKey()) {
208: pkMemberPositions[pk_num++] = i;
209: }
210: }
211: } else if (pk_field_count == 0
212: && identityType == IdentityType.APPLICATION) {
213: // No primary key fields found
214: throw new InvalidMetaDataException(LOCALISER, "044077",
215: fullName, objectidClass);
216: }
217:
218: nonPkMemberPositions = new int[total_field_count
219: - pk_field_count];
220: persistenceCapableMemberPositions = new int[pc_field_count];
221: for (int i = 0, npkf = 0, npc = 0; i < total_field_count; i++) {
222: AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
223: if (!fmd.isPrimaryKey()) {
224: nonPkMemberPositions[npkf++] = i;
225: }
226: if (fmd.isFieldTypePersistable()) {
227: persistenceCapableMemberPositions[npc++] = i;
228: }
229: }
230:
231: dfgMemberPositions = new int[dfg_field_count];
232: scoMutableMemberPositions = new int[scm_field_count];
233: for (int i = 0, dfg_num = 0, scm_num = 0; i < total_field_count; i++) {
234: if (dfgMemberFlags[i]) {
235: dfgMemberPositions[dfg_num++] = i;
236: }
237: if (scoMutableMemberFlags[i]) {
238: scoMutableMemberPositions[scm_num++] = i;
239: }
240: }
241:
242: joinMetaData = new JoinMetaData[joins.size()];
243: for (int i = 0; i < joinMetaData.length; i++) {
244: joinMetaData[i] = (JoinMetaData) joins.get(i);
245: joinMetaData[i].initialise();
246: }
247: indexMetaData = new IndexMetaData[indexes.size()];
248: for (int i = 0; i < indexMetaData.length; i++) {
249: indexMetaData[i] = (IndexMetaData) indexes.get(i);
250: indexMetaData[i].initialise();
251: }
252: foreignKeyMetaData = new ForeignKeyMetaData[foreignKeys.size()];
253: for (int i = 0; i < foreignKeyMetaData.length; i++) {
254: foreignKeyMetaData[i] = (ForeignKeyMetaData) foreignKeys
255: .get(i);
256: foreignKeyMetaData[i].initialise();
257: }
258: uniqueMetaData = new UniqueMetaData[uniqueConstraints.size()];
259: for (int i = 0; i < uniqueMetaData.length; i++) {
260: uniqueMetaData[i] = (UniqueMetaData) uniqueConstraints
261: .get(i);
262: uniqueMetaData[i].initialise();
263: }
264:
265: fetchGroupMetaData = new FetchGroupMetaData[fetchGroups.size()];
266: fetchGroupMetaDataByName = new HashMap();
267: for (int i = 0; i < fetchGroupMetaData.length; i++) {
268: fetchGroupMetaData[i] = (FetchGroupMetaData) fetchGroups
269: .get(i);
270: fetchGroupMetaData[i].initialise();
271: fetchGroupMetaDataByName.put(fetchGroupMetaData[i]
272: .getName(), fetchGroupMetaData[i]);
273: }
274:
275: // If using datastore id and user hasn't provided the identity element,
276: // add a defaulted one (using the superclass if available)
277: if (identityType == IdentityType.DATASTORE
278: && identityMetaData == null) {
279: if (pcSuperclassMetaData != null) {
280: IdentityMetaData super Imd = pcSuperclassMetaData
281: .getIdentityMetaData();
282: identityMetaData = new IdentityMetaData(this , super Imd
283: .getColumnName(), super Imd.getValueStrategy()
284: .toString(), super Imd.getSequence());
285: } else {
286: identityMetaData = new IdentityMetaData(this , null,
287: null, null);
288: }
289: }
290:
291: if (versionMetaData != null) {
292: versionMetaData.initialise();
293: }
294: if (identityMetaData != null) {
295: identityMetaData.initialise();
296: }
297: if (inheritanceMetaData != null) {
298: inheritanceMetaData.initialise();
299: }
300:
301: if (identityType == IdentityType.APPLICATION) {
302: usesSingleFieldIdentityClass = getMetaDataManager()
303: .getApiAdapter().isSingleFieldIdentityClass(
304: getObjectidClass());
305: }
306:
307: // Clear out the collections that we used when loading the MetaData
308: joins.clear();
309: joins = null;
310: fetchGroups.clear();
311: fetchGroups = null;
312: foreignKeys.clear();
313: foreignKeys = null;
314: indexes.clear();
315: indexes = null;
316: uniqueConstraints.clear();
317: uniqueConstraints = null;
318: setInitialised();
319: }
320:
321: /**
322: * Method to provide the details of the class being represented by this
323: * MetaData. This can be used to firstly provide defaults for attributes
324: * that aren't specified in the MetaData, and secondly to report any errors
325: * with attributes that have been specifed that are inconsistent with the
326: * class being represented.
327: * <P>
328: * One possible use of this method would be to take a basic ClassMetaData
329: * for a class and call this, passing in the users class. This would then
330: * add AbstractMemberMetaData for all fields in this class providing defaults for
331: * all of these.
332: *
333: * @param clr ClassLoaderResolver to use in loading any classes
334: * @param primary the primary ClassLoader to use (or null)
335: */
336: public synchronized void populate(ClassLoaderResolver clr,
337: ClassLoader primary) {
338: if (isInitialised() || isPopulated()) {
339: JPOXLogger.METADATA.error(LOCALISER.msg("044068", name));
340: throw new JPOXException(LOCALISER.msg("044068", fullName))
341: .setFatal();
342: }
343: if (populating) {
344: return;
345: }
346:
347: try {
348: if (JPOXLogger.METADATA.isDebugEnabled()) {
349: JPOXLogger.METADATA.debug(LOCALISER.msg("044075",
350: fullName));
351: }
352: populating = true;
353:
354: Class cls = loadClass(clr, primary);
355:
356: // Load any Annotations definition for this class
357: if (!isMetaDataComplete()) {
358: getMetaDataManager().addAnnotationsDataToClass(cls,
359: this , clr);
360: }
361:
362: // Load any ORM definition for this class
363: getMetaDataManager().addORMDataToClass(cls, clr);
364:
365: // If a class is an inner class and is non-static it is invalid
366: if (ClassUtils.isInnerClass(fullName)
367: && !Modifier.isStatic(cls.getModifiers())
368: && persistenceModifier == ClassPersistenceModifier.PERSISTENCE_CAPABLE) {
369: throw new InvalidMetaDataException(LOCALISER, "044063",
370: fullName);
371: }
372:
373: determineSuperClassName(clr, cls);
374:
375: inheritDetachableSettings();
376:
377: inheritIdentity();
378:
379: determineIdentity();
380:
381: validateUserInputForIdentity();
382:
383: addMetaDataForMembersNotInMetaData(cls);
384:
385: if (objectidClass == null) {
386: // No user-defined objectid-class but potentially have SingleFieldIdentity so make sure PK fields are set
387: populatePropertyMetaData(clr, cls, true, primary); // Make sure all PK fields (and superclasses) are set before objectid-class
388: determineObjectIdClass(clr);
389: populatePropertyMetaData(clr, cls, false, primary); // Populate non-PK fields
390: } else {
391: populatePropertyMetaData(clr, cls, true, primary);
392: populatePropertyMetaData(clr, cls, false, primary);
393: determineObjectIdClass(clr);
394: }
395:
396: validateUserInputForInheritanceMetaData();
397:
398: determineInheritanceMetaData();
399:
400: validateDeprecatedMetaData();
401:
402: setPopulated();
403: } catch (RuntimeException e) {
404: JPOXLogger.METADATA.debug(e);
405: throw e;
406: } finally {
407: populating = false;
408: }
409: }
410:
411: /**
412: * Utility to add a defaulted PropertyMetaData to the class.
413: * Provided as a method since then any derived classes can override it.
414: * @param name name of field
415: * @return the new PropertyMetaData
416: */
417: protected AbstractMemberMetaData newDefaultedProperty(String name) {
418: return new PropertyMetaData(this , name);
419: }
420:
421: /**
422: * Populate PropertyMetaData.
423: * @param clr The ClassLoader
424: * @param cls This class
425: * @param pkFields Process pk fields (or non-PK fields if false)
426: * @param primary the primary ClassLoader to use (or null)
427: * @throws InvalidMetaDataException if the Class for a declared type in a field cannot be loaded by the <code>clr</code>
428: * @throws InvalidMetaDataException if a field declared in the MetaData does not exist in the Class
429: */
430: protected void populatePropertyMetaData(ClassLoaderResolver clr,
431: Class cls, boolean pkFields, ClassLoader primary) {
432: Collections.sort(members);
433:
434: // Populate the AbstractMemberMetaData with their real field values
435: // This will populate any containers in these fields also
436: Iterator fields_iter = members.iterator();
437: while (fields_iter.hasNext()) {
438: AbstractMemberMetaData fmd = (AbstractMemberMetaData) fields_iter
439: .next();
440: if (pkFields == fmd.isPrimaryKey()) {
441: Class fieldCls = cls;
442: if (!fmd.fieldBelongsToClass()) {
443: // Field overrides a field in a superclass, so find the class
444: try {
445: fieldCls = clr.classForName(fmd.getClassName(),
446: primary);
447: } catch (ClassNotResolvedException cnre) {
448: // Not found at specified location, so try the same package as this class
449: String fieldClassName = getPackageName() + "."
450: + fmd.getClassName();
451: try {
452: fieldCls = clr.classForName(fieldClassName,
453: primary);
454: fmd.setClassName(fieldClassName);
455: } catch (ClassNotResolvedException cnre2) {
456: JPOXLogger.METADATA.error(LOCALISER.msg(
457: "044080", fieldClassName));
458: throw new InvalidMetaDataException(
459: LOCALISER, "044080", fieldClassName);
460: }
461: }
462: }
463:
464: Method cls_method = null;
465: try {
466: try {
467: cls_method = fieldCls.getDeclaredMethod(
468: ClassUtils.getJavaBeanGetterName(fmd
469: .getName(), true), null);
470: } catch (Exception e) {
471: cls_method = fieldCls.getDeclaredMethod(
472: ClassUtils.getJavaBeanGetterName(fmd
473: .getName(), false), null);
474: }
475: } catch (Exception e) {
476: // MetaData method doesn't exist in the class!
477: throw new InvalidMetaDataException(LOCALISER,
478: "044072", fullName, fmd.getFullFieldName());
479: }
480: fmd.populate(clr, null, cls_method, primary);
481: }
482: }
483: }
484:
485: /**
486: * Add MetaData for properties of the interface not declared in MetaData.
487: * @param cls Class represented by this metadata
488: */
489: protected void addMetaDataForMembersNotInMetaData(Class cls) {
490: // Add MetaData for properties for the interface that aren't in the XML/annotations.
491: // We use Reflection here since JDOImplHelper would only give use info
492: // for enhanced files (and the enhancer needs unenhanced as well).
493: // NOTE 1 : We ignore properties in superclasses
494: // NOTE 2 : We ignore "enhanced" properties (added by the enhancer)
495: // NOTE 3 : We ignore inner class fields (containing "$")
496: // NOTE 4 : We sort the properties into ascending alphabetical order
497: Collections.sort(members);
498: try {
499: // Get all (reflected) methods in the populating class
500: Method[] clsMethods = cls.getDeclaredMethods();
501: for (int i = 0; i < clsMethods.length; i++) {
502: // Limit to getter methods in this class, that aren't inner class methods, and that aren't static
503: if (clsMethods[i].getDeclaringClass().getName().equals(
504: fullName)
505: && (clsMethods[i].getName().startsWith("get") || clsMethods[i]
506: .getName().startsWith("is"))
507: && !ClassUtils.isInnerClass(clsMethods[i]
508: .getName())
509: && !Modifier.isStatic(clsMethods[i]
510: .getModifiers())) {
511: // Find if there is metadata for this property
512: String fieldName = ClassUtils
513: .getFieldNameForJavaBeanGetter(clsMethods[i]
514: .getName());
515: if (Collections.binarySearch(members, fieldName) < 0) // PropertyMetaData implements Comparable
516: {
517: // Check if a setter is also present
518: String setterName = ClassUtils
519: .getJavaBeanSetterName(fieldName);
520: for (int j = 0; j < clsMethods.length; j++) {
521: if (clsMethods[j].getName().equals(
522: setterName)) {
523: // Getter/Setter for a property but not in MetaData so add
524: JPOXLogger.METADATA
525: .debug(LOCALISER.msg("044060",
526: fieldName, name));
527: AbstractMemberMetaData mmd = newDefaultedProperty(fieldName);
528: members.add(mmd);
529: Collections.sort(members);
530: break;
531: }
532: }
533: }
534: }
535: }
536: } catch (Exception e) {
537: JPOXLogger.METADATA.error(e.getMessage(), e);
538: throw new JPOXUserException(e.getMessage());
539: }
540: }
541:
542: // ------------------------------ Utilities --------------------------------
543:
544: /**
545: * Returns a string representation of the object.
546: * This can be used as part of a facility to output a MetaData file.
547: * @param prefix prefix string
548: * @param indent indent string
549: * @return a string representation of the object.
550: */
551: public String toString(String prefix, String indent) {
552: StringBuffer sb = new StringBuffer();
553: sb.append(prefix).append("<interface name=\"" + name + "\"\n");
554: if (identityType != null) {
555: sb.append(prefix).append(
556: " identity-type=\"" + identityType + "\"\n");
557: }
558: if (objectidClass != null) {
559: sb.append(prefix)
560: .append(
561: " objectid-class=\"" + objectidClass
562: + "\"\n");
563: }
564: if (!requiresExtent) {
565: sb.append(prefix)
566: .append(" requires-extent=\"false\"");
567: }
568: if (embeddedOnly) {
569: sb.append(prefix).append(" embedded-only=\"true\"\n");
570: }
571: if (detachable) {
572: sb.append(prefix).append(" detachable=\"true\"\n");
573: }
574: if (table != null) {
575: sb.append(prefix)
576: .append(" table=\"" + table + "\"\n");
577: }
578: sb.append(">\n");
579:
580: // Identity
581: if (identityMetaData != null) {
582: sb.append(identityMetaData
583: .toString(prefix + indent, indent));
584: }
585:
586: // PrimaryKey
587: if (primaryKeyMetaData != null) {
588: sb.append(primaryKeyMetaData.toString(prefix + indent,
589: indent));
590: }
591:
592: // Inheritance
593: if (inheritanceMetaData != null) {
594: sb.append(inheritanceMetaData.toString(prefix + indent,
595: indent));
596: }
597:
598: // Version
599: if (versionMetaData != null) {
600: sb
601: .append(versionMetaData.toString(prefix + indent,
602: indent));
603: }
604:
605: // Add joins
606: if (joins != null) {
607: for (int i = 0; i < joins.size(); i++) {
608: JoinMetaData jmd = (JoinMetaData) joins.get(i);
609: sb.append(jmd.toString(prefix + indent, indent));
610: }
611: }
612:
613: // Add foreign-keys
614: if (foreignKeys != null) {
615: for (int i = 0; i < foreignKeys.size(); i++) {
616: ForeignKeyMetaData fkmd = (ForeignKeyMetaData) foreignKeys
617: .get(i);
618: sb.append(fkmd.toString(prefix + indent, indent));
619: }
620: }
621:
622: // Add indexes
623: if (indexes != null) {
624: for (int i = 0; i < indexes.size(); i++) {
625: IndexMetaData imd = (IndexMetaData) indexes.get(i);
626: sb.append(imd.toString(prefix + indent, indent));
627: }
628: }
629:
630: // Add unique constraints
631: if (uniqueConstraints != null) {
632: for (int i = 0; i < uniqueConstraints.size(); i++) {
633: UniqueMetaData unimd = (UniqueMetaData) uniqueConstraints
634: .get(i);
635: sb.append(unimd.toString(prefix + indent, indent));
636: }
637: }
638:
639: // Add properties
640: if (members != null) {
641: for (int i = 0; i < members.size(); i++) {
642: PropertyMetaData pmd = (PropertyMetaData) members
643: .get(i);
644: sb.append(pmd.toString(prefix + indent, indent));
645: }
646: }
647:
648: // Add queries
649: if (queries != null) {
650: Iterator iter = queries.iterator();
651: while (iter.hasNext()) {
652: QueryMetaData q = (QueryMetaData) iter.next();
653: sb.append(q.toString(prefix + indent, indent));
654: }
655: }
656:
657: // Add fetch-groups
658: if (fetchGroups != null) {
659: for (int i = 0; i < fetchGroups.size(); i++) {
660: FetchGroupMetaData fgmd = (FetchGroupMetaData) fetchGroups
661: .get(i);
662: sb.append(fgmd.toString(prefix + indent, indent));
663: }
664: }
665:
666: // Add extensions
667: sb.append(super .toString(prefix + indent, indent));
668:
669: sb.append(prefix + "</interface>\n");
670: return sb.toString();
671: }
672: }
|