001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc.metadata;
023:
024: import java.lang.reflect.Field;
025: import java.lang.reflect.Method;
026: import java.lang.reflect.Modifier;
027: import java.util.ArrayList;
028: import java.util.Collections;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: import org.jboss.deployment.DeploymentException;
033: import org.jboss.metadata.MetaData;
034:
035: import org.w3c.dom.Element;
036:
037: /**
038: * Imutable class which holds all the information jbosscmp-jdbc needs to know
039: * about a CMP field It loads its data from standardjbosscmp-jdbc.xml and
040: * jbosscmp-jdbc.xml
041: *
042: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
043: * @author <a href="sebastien.alborini@m4x.org">Sebastien Alborini</a>
044: * @author <a href="mailto:dirk@jboss.de">Dirk Zimmermann</a>
045: * @author <a href="mailto:vincent.harcq@hubmethods.com">Vincent Harcq</a>
046: * @author <a href="mailto:loubyansky@hotmail.com">Alex Loubyansky</a>
047: * @author <a href="mailto:heiko.rupp@cellent.de">Heiko W.Rupp</a>
048: *
049: * @version $Revision: 57209 $
050: */
051: public final class JDBCCMPFieldMetaData {
052: public static final byte CHECK_DIRTY_AFTER_GET_TRUE = 1;
053: public static final byte CHECK_DIRTY_AFTER_GET_FALSE = 2;
054: public static final byte CHECK_DIRTY_AFTER_GET_NOT_PRESENT = 4;
055:
056: /** The entity on which this field is defined. */
057: private final JDBCEntityMetaData entity;
058:
059: /** The name of this field. */
060: private final String fieldName;
061:
062: /** The java type of this field */
063: private final Class fieldType;
064:
065: /** The column name in the table */
066: private final String columnName;
067:
068: /**
069: * The jdbc type (see java.sql.Types), used in PreparedStatement.setParameter
070: * default value used is intended to cause an exception if used
071: */
072: private final int jdbcType;
073:
074: /** The sql type, used for table creation. */
075: private final String sqlType;
076:
077: /** Is this field read only? */
078: private final boolean readOnly;
079:
080: /** How long is read valid */
081: private final int readTimeOut;
082:
083: /** Is this field a memeber of the primary keys or the sole prim-key-field. */
084: private final boolean primaryKeyMember;
085:
086: /** Should null values not be allowed for this field. */
087: private final boolean notNull;
088:
089: /** Should an index for this field be generated? */
090: private final boolean genIndex;
091:
092: /**
093: * The Field object in the primary key class for this
094: * cmp field, or null if this field is the prim-key-field.
095: */
096: private final Field primaryKeyField;
097:
098: /** property overrides */
099: private final List propertyOverrides = new ArrayList();
100:
101: /** indicates whether this is an unknown pk field */
102: private final boolean unknownPkField;
103:
104: /** auto-increment flag */
105: private final boolean autoIncrement;
106:
107: /** whether this field is a relation table key field*/
108: private final boolean relationTableField;
109:
110: /** If true, the field should be checked for dirty state after its get method was invoked */
111: private final byte checkDirtyAfterGet;
112:
113: /** Fully qualified class name of implementation of CMPFieldStateFactory */
114: private final String stateFactory;
115:
116: private static byte readCheckDirtyAfterGet(Element element,
117: byte defaultValue) throws DeploymentException {
118: byte checkDirtyAfterGet;
119: String dirtyAfterGetStr = MetaData.getOptionalChildContent(
120: element, "check-dirty-after-get");
121: if (dirtyAfterGetStr == null) {
122: checkDirtyAfterGet = defaultValue;
123: } else {
124: checkDirtyAfterGet = (Boolean.valueOf(dirtyAfterGetStr)
125: .booleanValue() ? CHECK_DIRTY_AFTER_GET_TRUE
126: : CHECK_DIRTY_AFTER_GET_FALSE);
127: }
128: return checkDirtyAfterGet;
129: }
130:
131: public static byte readCheckDirtyAfterGet(Element element)
132: throws DeploymentException {
133: return readCheckDirtyAfterGet(element,
134: CHECK_DIRTY_AFTER_GET_NOT_PRESENT);
135: }
136:
137: /**
138: * This constructor is added especially for unknown primary key field
139: */
140: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity) {
141: this .entity = entity;
142: fieldName = entity.getName() + "_upk";
143: fieldType = entity.getPrimaryKeyClass(); // java.lang.Object.class
144: columnName = entity.getName() + "_upk";
145: jdbcType = Integer.MIN_VALUE;
146: sqlType = null;
147: readOnly = entity.isReadOnly();
148: readTimeOut = entity.getReadTimeOut();
149: primaryKeyMember = true;
150: notNull = true;
151: primaryKeyField = null;
152: genIndex = false;
153: unknownPkField = true;
154: autoIncrement = false;
155: relationTableField = false;
156: checkDirtyAfterGet = CHECK_DIRTY_AFTER_GET_NOT_PRESENT;
157: stateFactory = null;
158: }
159:
160: /**
161: * Constructs cmp field meta data for a field on the specified entity with
162: * the specified fieldName.
163: *
164: * @param fieldName name of the field for which the meta data will be loaded
165: * @param entity entity on which this field is defined
166: * @throws DeploymentException if data in the entity is inconsistent with field type
167: */
168: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
169: String fieldName) throws DeploymentException {
170: this .entity = entity;
171: this .fieldName = fieldName;
172:
173: fieldType = loadFieldType(entity, fieldName);
174: columnName = fieldName;
175: jdbcType = Integer.MIN_VALUE;
176: sqlType = null;
177: readOnly = entity.isReadOnly();
178: readTimeOut = entity.getReadTimeOut();
179: genIndex = false;
180:
181: // initialize primary key info
182: String pkFieldName = entity.getPrimaryKeyFieldName();
183: if (pkFieldName != null) {
184: // single-valued key so field is null
185: primaryKeyField = null;
186:
187: // is this the pk field
188: if (pkFieldName.equals(fieldName)) {
189: // verify field type
190: if (!entity.getPrimaryKeyClass().equals(fieldType)) {
191: throw new DeploymentException(
192: "primkey-field must be the same type as prim-key-class");
193: }
194: // we are the pk
195: primaryKeyMember = true;
196: } else {
197: primaryKeyMember = false;
198: }
199: } else {
200: // this is a multi-valued key
201: Field[] fields = entity.getPrimaryKeyClass().getFields();
202:
203: boolean pkMember = false;
204: Field pkField = null;
205: for (int i = 0; i < fields.length; i++) {
206: final Field field = fields[i];
207: if (field.getName().equals(fieldName)) {
208:
209: // verify field type
210: if (!field.getType().equals(fieldType)) {
211: throw new DeploymentException(
212: "Field "
213: + fieldName
214: + " in prim-key-class must be of the same type.");
215: }
216:
217: if (pkField != null) {
218: if (field.getDeclaringClass().equals(
219: entity.getPrimaryKeyClass())) {
220: pkField = field;
221: }
222:
223: org.jboss.logging.Logger
224: .getLogger(
225: getClass().getName() + '.'
226: + entity.getName())
227: .warn(
228: "PK field "
229: + fieldName
230: + " was found more than once in class hierarchy of "
231: + entity
232: .getPrimaryKeyClass()
233: .getName()
234: + ". Will use the one from "
235: + pkField
236: .getDeclaringClass()
237: .getName());
238: } else {
239: pkField = field;
240: }
241:
242: // we are a pk member
243: pkMember = true;
244: }
245: }
246: primaryKeyMember = pkMember;
247: primaryKeyField = pkField;
248: }
249: notNull = fieldType.isPrimitive() || primaryKeyMember;
250:
251: unknownPkField = false;
252: autoIncrement = false;
253: relationTableField = false;
254: checkDirtyAfterGet = CHECK_DIRTY_AFTER_GET_NOT_PRESENT;
255: stateFactory = null;
256: }
257:
258: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
259: JDBCCMPFieldMetaData defaultValues) {
260: this .entity = entity;
261: fieldName = defaultValues.getFieldName();
262: fieldType = defaultValues.getFieldType();
263: columnName = defaultValues.getColumnName();
264: jdbcType = defaultValues.getJDBCType();
265: sqlType = defaultValues.getSQLType();
266: readOnly = entity.isReadOnly();
267: readTimeOut = entity.getReadTimeOut();
268: primaryKeyMember = defaultValues.isPrimaryKeyMember();
269: primaryKeyField = defaultValues.getPrimaryKeyField();
270: notNull = defaultValues.isNotNull();
271: unknownPkField = defaultValues.isUnknownPkField();
272: autoIncrement = defaultValues.isAutoIncrement();
273: genIndex = false; // If <dbindex/> is not given on a field, no index is wanted.
274: relationTableField = defaultValues.isRelationTableField();
275: checkDirtyAfterGet = defaultValues.getCheckDirtyAfterGet();
276: stateFactory = defaultValues.getStateFactory();
277: }
278:
279: /**
280: * Constructs cmp field meta data with the data contained in the cmp-field
281: * xml element from a jbosscmp-jdbc xml file. Optional values of the xml
282: * element that are not present are instead loaded from the defalutValues
283: * parameter.
284: *
285: * @param element the xml Element which contains the metadata about
286: * this field
287: * @param defaultValues the JDBCCMPFieldMetaData which contains the values
288: * for optional elements of the element
289: * @throws DeploymentException if the xml element is not semantically correct
290: */
291: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
292: Element element, JDBCCMPFieldMetaData defaultValues)
293: throws DeploymentException {
294: this .entity = entity;
295:
296: // unknown primary key
297: this .unknownPkField = defaultValues.isUnknownPkField();
298:
299: // Field name
300: // if field-name is specified for unknown-pk, it's set here
301: String unknownFieldName = MetaData.getOptionalChildContent(
302: element, "field-name");
303: if (unknownPkField && unknownFieldName != null) {
304: fieldName = unknownFieldName;
305: } else {
306: fieldName = defaultValues.getFieldName();
307: }
308:
309: // Field type
310: // must be set for unknow-pk
311: String unknownPkClass = MetaData.getOptionalChildContent(
312: element, "unknown-pk-class");
313: if (unknownPkClass == null) {
314: fieldType = defaultValues.getFieldType();
315: } else {
316: try {
317: fieldType = entity.getClassLoader().loadClass(
318: unknownPkClass);
319: } catch (ClassNotFoundException e) {
320: throw new DeploymentException(
321: "could not load the class for "
322: + " unknown primary key: "
323: + unknownPkClass);
324: }
325: }
326:
327: // Column name
328: String columnStr = MetaData.getOptionalChildContent(element,
329: "column-name");
330: if (columnStr != null) {
331: columnName = columnStr;
332: } else {
333: columnName = defaultValues.getColumnName();
334: }
335:
336: // JDBC Type
337: String jdbcStr = MetaData.getOptionalChildContent(element,
338: "jdbc-type");
339: if (jdbcStr != null) {
340: jdbcType = JDBCMappingMetaData.getJdbcTypeFromName(jdbcStr);
341: // SQL Type
342: sqlType = MetaData.getUniqueChildContent(element,
343: "sql-type");
344: } else {
345: jdbcType = defaultValues.getJDBCType();
346: sqlType = defaultValues.getSQLType();
347: }
348:
349: // read-only
350: String readOnlyStr = MetaData.getOptionalChildContent(element,
351: "read-only");
352: if (readOnlyStr != null) {
353: readOnly = Boolean.valueOf(readOnlyStr).booleanValue();
354: } else {
355: readOnly = defaultValues.isReadOnly();
356: }
357:
358: // read-time-out
359: String readTimeOutStr = MetaData.getOptionalChildContent(
360: element, "read-time-out");
361: if (readTimeOutStr != null) {
362: try {
363: readTimeOut = Integer.parseInt(readTimeOutStr);
364: } catch (NumberFormatException e) {
365: throw new DeploymentException(
366: "Invalid number format in " + "read-time-out '"
367: + readTimeOutStr + "': " + e);
368: }
369: } else {
370: readTimeOut = defaultValues.getReadTimeOut();
371: }
372:
373: // primary key member?
374: this .primaryKeyMember = defaultValues.isPrimaryKeyMember();
375:
376: // field object of the primary key
377: primaryKeyField = defaultValues.getPrimaryKeyField();
378:
379: // not-null
380: Element notNullElement = MetaData.getOptionalChild(element,
381: "not-null");
382: notNull = fieldType.isPrimitive() || primaryKeyMember
383: || (notNullElement != null);
384:
385: // property overrides
386: Iterator iterator = MetaData.getChildrenByTagName(element,
387: "property");
388: while (iterator.hasNext()) {
389: propertyOverrides.add(new JDBCCMPFieldPropertyMetaData(
390: this , (Element) iterator.next()));
391: }
392:
393: // is the field auto-increment?
394: autoIncrement = MetaData.getOptionalChild(element,
395: "auto-increment") != null;
396:
397: // should an index for this field be generated?
398: if (MetaData.getOptionalChild(element, "dbindex") == null)
399: genIndex = false;
400: else
401: genIndex = true;
402:
403: relationTableField = defaultValues.isRelationTableField();
404:
405: checkDirtyAfterGet = readCheckDirtyAfterGet(element,
406: defaultValues.getCheckDirtyAfterGet());
407:
408: String stateFactoryStr = MetaData.getOptionalChildContent(
409: element, "state-factory");
410: if (stateFactoryStr == null)
411: stateFactory = defaultValues.getStateFactory();
412: else
413: stateFactory = stateFactoryStr;
414: }
415:
416: /**
417: * Constructs cmp field meta data with the data contained in the cmp-field
418: * xml element from a jbosscmp-jdbc xml file. Optional values of the xml
419: * element that are not present are instead loaded from the defalutValues
420: * parameter.
421: *
422: * This constructor form is used to create cmp field meta data for use as
423: * foreign keys. The primaryKeyMember parameter is very important in this
424: * context because a foreign key is not a primary key member but used a pk
425: * member as the default value. If we did not have the primary key member
426: * parameter this JDBCCMPFieldMetaData would get the value from the
427: * defaultValues and be declared a memeber.
428: */
429: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
430: Element element, JDBCCMPFieldMetaData defaultValues,
431: boolean primaryKeyMember, boolean notNull,
432: boolean readOnly, int readTimeOut,
433: boolean relationTableField) throws DeploymentException {
434: this .entity = entity;
435: fieldName = defaultValues.getFieldName();
436: fieldType = defaultValues.getFieldType();
437: String columnStr = MetaData.getOptionalChildContent(element,
438: "column-name");
439: if (columnStr != null) {
440: columnName = columnStr;
441: } else {
442: columnName = defaultValues.getColumnName();
443: }
444:
445: // JDBC Type
446: String jdbcStr = MetaData.getOptionalChildContent(element,
447: "jdbc-type");
448: if (jdbcStr != null) {
449: jdbcType = JDBCMappingMetaData.getJdbcTypeFromName(jdbcStr);
450: sqlType = MetaData.getUniqueChildContent(element,
451: "sql-type");
452: } else {
453: jdbcType = defaultValues.getJDBCType();
454: sqlType = defaultValues.getSQLType();
455: }
456:
457: // read-only
458: this .readOnly = readOnly;
459:
460: // read-time-out
461: this .readTimeOut = readTimeOut;
462:
463: // primary key member?
464: this .primaryKeyMember = primaryKeyMember;
465:
466: // not-null
467: this .notNull = notNull;
468:
469: // field object of the primary key
470: primaryKeyField = defaultValues.getPrimaryKeyField();
471:
472: // property overrides
473: Iterator iterator = MetaData.getChildrenByTagName(element,
474: "property");
475: while (iterator.hasNext()) {
476: propertyOverrides.add(new JDBCCMPFieldPropertyMetaData(
477: this , (Element) iterator.next()));
478: }
479:
480: this .unknownPkField = defaultValues.isUnknownPkField();
481: autoIncrement = MetaData.getOptionalChild(element,
482: "auto-increment") != null;
483:
484: if (MetaData.getOptionalChild(element, "dbindex") == null)
485: genIndex = false;
486: else
487: genIndex = true;
488:
489: this .relationTableField = relationTableField;
490:
491: String dirtyAfterGetStr = MetaData.getOptionalChildContent(
492: element, "check-dirty-after-get");
493: if (dirtyAfterGetStr == null) {
494: checkDirtyAfterGet = defaultValues.getCheckDirtyAfterGet();
495: } else {
496: checkDirtyAfterGet = (Boolean.valueOf(dirtyAfterGetStr)
497: .booleanValue() ? CHECK_DIRTY_AFTER_GET_TRUE
498: : CHECK_DIRTY_AFTER_GET_FALSE);
499: }
500:
501: String stateFactoryStr = MetaData.getOptionalChildContent(
502: element, "state-factory");
503: if (stateFactoryStr == null)
504: stateFactory = defaultValues.getStateFactory();
505: else
506: stateFactory = stateFactoryStr;
507: }
508:
509: /**
510: * Constructs a foreign key or a relation table key field.
511: */
512: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
513: JDBCCMPFieldMetaData defaultValues, String columnName,
514: boolean primaryKeyMember, boolean notNull,
515: boolean readOnly, int readTimeOut,
516: boolean relationTableField) {
517: this .entity = entity;
518: fieldName = defaultValues.getFieldName();
519: fieldType = defaultValues.getFieldType();
520: this .columnName = columnName;
521: jdbcType = defaultValues.getJDBCType();
522: sqlType = defaultValues.getSQLType();
523: this .readOnly = readOnly;
524: this .readTimeOut = readTimeOut;
525: this .primaryKeyMember = primaryKeyMember;
526: primaryKeyField = defaultValues.getPrimaryKeyField();
527: this .notNull = notNull;
528:
529: for (Iterator i = defaultValues.propertyOverrides.iterator(); i
530: .hasNext();) {
531: propertyOverrides.add(new JDBCCMPFieldPropertyMetaData(
532: this , (JDBCCMPFieldPropertyMetaData) i.next()));
533: }
534:
535: this .unknownPkField = defaultValues.isUnknownPkField();
536: autoIncrement = false;
537: genIndex = false;
538:
539: this .relationTableField = relationTableField;
540: checkDirtyAfterGet = defaultValues.getCheckDirtyAfterGet();
541: stateFactory = defaultValues.getStateFactory();
542: }
543:
544: /**
545: * Constructs a field that is used as an optimistic lock
546: */
547: public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
548: String fieldName, Class fieldType, String columnName,
549: int jdbcType, String sqlType) throws DeploymentException {
550: this .entity = entity;
551: this .fieldName = fieldName;
552: this .fieldType = fieldType;
553: this .columnName = columnName;
554: this .jdbcType = jdbcType;
555: this .sqlType = sqlType;
556: readOnly = false;
557: readTimeOut = -1;
558: primaryKeyMember = false;
559: notNull = true;
560: primaryKeyField = null;
561: unknownPkField = false;
562: autoIncrement = false;
563: genIndex = false;
564: relationTableField = false;
565: checkDirtyAfterGet = CHECK_DIRTY_AFTER_GET_NOT_PRESENT;
566: stateFactory = null;
567: }
568:
569: /**
570: * Gets the entity on which this field is defined
571: * @return the entity on which this field is defined
572: */
573: public JDBCEntityMetaData getEntity() {
574: return entity;
575: }
576:
577: /**
578: * Gets the name of the field.
579: * @return the name of this field
580: */
581: public String getFieldName() {
582: return fieldName;
583: }
584:
585: /**
586: * Gets the java Class type of this field.
587: * @return the Class type of this field
588: */
589: public Class getFieldType() {
590: return fieldType;
591: }
592:
593: /**
594: * Gets the column name the property should use or null if the
595: * column name is not overriden.
596: * @return the name to which this field is persisted or null if the
597: * column name is not overriden
598: */
599: public String getColumnName() {
600: return columnName;
601: }
602:
603: /**
604: * Gets the JDBC type the property should use or Integer.MIN_VALUE
605: * if not overriden.
606: * @return the jdbc type of this field
607: */
608: public int getJDBCType() {
609: return jdbcType;
610: }
611:
612: /**
613: * Gets the SQL type the property should use or null
614: * if not overriden.
615: * @return the sql data type string used in create table statements
616: */
617: public String getSQLType() {
618: return sqlType;
619: }
620:
621: /**
622: * Gets the property overrides. Property overrides change the default
623: * mapping of Dependent Value Object properties. If there are no property
624: * overrides this method returns an empty list.
625: * @return an unmodifiable list of the property overrides.
626: */
627: public List getPropertyOverrides() {
628: return Collections.unmodifiableList(propertyOverrides);
629: }
630:
631: /**
632: * Is this field read only. A read only field will never be persisted
633: *
634: * @return true if this field is read only
635: */
636: public boolean isReadOnly() {
637: return readOnly;
638: }
639:
640: /**
641: * Gets the length of time (ms) that a read valid or -1 if data must
642: * always be reread from the database
643: * @return the length of time that data read database is valid, or -1
644: * if data must always be reread from the database
645: */
646: public int getReadTimeOut() {
647: return readTimeOut;
648: }
649:
650: /**
651: * Is this field one of the primary key fields?
652: * @return true if this field is one of the primary key fields
653: */
654: public boolean isPrimaryKeyMember() {
655: return primaryKeyMember;
656: }
657:
658: /**
659: * Should this field allow null values?
660: * @return true if this field will not allow a null value.
661: */
662: public boolean isNotNull() {
663: return notNull;
664: }
665:
666: /**
667: * Should an index for this field be generated?
668: * Normally this should be false for primary key fields
669: * But it seems there are databases that do not automatically
670: * put indices on primary keys *sigh*
671: * @return true if an index should be generated on this field
672: */
673: public boolean isIndexed() {
674: return genIndex;
675: }
676:
677: /**
678: * Gets the Field of the primary key object which contains the value of
679: * this field. Returns null, if this field is not a member of the primary
680: * key, or if the primray key is single valued.
681: * @return the Field of the primary key which contains the
682: * value of this field
683: */
684: public Field getPrimaryKeyField() {
685: return primaryKeyField;
686: }
687:
688: /**
689: * Is this field an unknown primary key field?
690: * @return true if the field is an unknown primary key field
691: */
692: public boolean isUnknownPkField() {
693: return unknownPkField;
694: }
695:
696: /**
697: * @return true if the key is auto incremented by the database
698: */
699: public boolean isAutoIncrement() {
700: return autoIncrement;
701: }
702:
703: public boolean isRelationTableField() {
704: return relationTableField;
705: }
706:
707: public byte getCheckDirtyAfterGet() {
708: return checkDirtyAfterGet;
709: }
710:
711: public String getStateFactory() {
712: return stateFactory;
713: }
714:
715: /**
716: * Compares this JDBCCMPFieldMetaData against the specified object. Returns
717: * true if the objects are the same. Two JDBCCMPFieldMetaData are the same
718: * if they both have the same name and are defined on the same entity.
719: * @param o the reference object with which to compare
720: * @return true if this object is the same as the object argument; false
721: * otherwise
722: */
723: public boolean equals(Object o) {
724: if (o instanceof JDBCCMPFieldMetaData) {
725: JDBCCMPFieldMetaData cmpField = (JDBCCMPFieldMetaData) o;
726: return fieldName.equals(cmpField.fieldName)
727: && entity.equals(cmpField.entity);
728: }
729: return false;
730: }
731:
732: /**
733: * Returns a hashcode for this JDBCCMPFieldMetaData. The hashcode is computed
734: * based on the hashCode of the declaring entity and the hashCode of the
735: * fieldName
736: * @return a hash code value for this object
737: */
738: public int hashCode() {
739: int result = 17;
740: result = 37 * result + entity.hashCode();
741: result = 37 * result + fieldName.hashCode();
742: return result;
743: }
744:
745: /**
746: * Returns a string describing this JDBCCMPFieldMetaData. The exact details
747: * of the representation are unspecified and subject to change, but the
748: * following may be regarded as typical:
749: *
750: * "[JDBCCMPFieldMetaData: fieldName=name, [JDBCEntityMetaData:
751: * entityName=UserEJB]]"
752: *
753: * @return a string representation of the object
754: */
755: public String toString() {
756: return "[JDBCCMPFieldMetaData : fieldName=" + fieldName + ", "
757: + entity + "]";
758: }
759:
760: /**
761: * Loads the java type of this field from the entity bean class. If this
762: * bean uses, cmp 1.x persistence, the field type is loaded from the field
763: * in the bean class with the same name as this field. If this bean uses,
764: * cmp 2.x persistence, the field type is loaded from the abstract getter
765: * or setter method for field in the bean class.
766: */
767: private Class loadFieldType(JDBCEntityMetaData entity,
768: String fieldName) throws DeploymentException {
769: if (entity.isCMP1x()) {
770: // CMP 1.x field Style
771: try {
772: return entity.getEntityClass().getField(fieldName)
773: .getType();
774: } catch (NoSuchFieldException e) {
775: throw new DeploymentException("No field named '"
776: + fieldName + "' found in entity class."
777: + entity.getEntityClass().getName());
778: }
779: } else {
780: // CMP 2.x abstract accessor style
781: String baseName = Character
782: .toUpperCase(fieldName.charAt(0))
783: + fieldName.substring(1);
784: String getName = "get" + baseName;
785: String setName = "set" + baseName;
786:
787: Method[] methods = entity.getEntityClass().getMethods();
788: for (int i = 0; i < methods.length; i++) {
789: // is this a public abstract method?
790: if (Modifier.isPublic(methods[i].getModifiers())
791: && Modifier.isAbstract(methods[i]
792: .getModifiers())) {
793:
794: // get accessor
795: if (getName.equals(methods[i].getName())
796: && methods[i].getParameterTypes().length == 0
797: && !methods[i].getReturnType().equals(
798: Void.TYPE)) {
799: return methods[i].getReturnType();
800: }
801:
802: // set accessor
803: if (setName.equals(methods[i].getName())
804: && methods[i].getParameterTypes().length == 1
805: && methods[i].getReturnType().equals(
806: Void.TYPE)) {
807:
808: return methods[i].getParameterTypes()[0];
809: }
810: }
811: }
812: throw new DeploymentException(
813: "No abstract accessors for field " + "named '"
814: + fieldName + "' found in entity class "
815: + entity.getEntityClass().getName());
816: }
817: }
818: }
|