001: /*
002: $Header: /cvsroot/xorm/xorm/src/org/xorm/ModelMapping.java,v 1.57 2004/05/04 18:57:09 wbiggs Exp $
003:
004: This file is part of XORM.
005:
006: XORM is free software; you can redistribute it and/or modify
007: it under the terms of the GNU General Public License as published by
008: the Free Software Foundation; either version 2 of the License, or
009: (at your option) any later version.
010:
011: XORM is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU General Public License for more details.
015:
016: You should have received a copy of the GNU General Public License
017: along with XORM; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package org.xorm;
021:
022: import java.beans.PropertyDescriptor;
023: import java.io.ByteArrayInputStream;
024: import java.io.ByteArrayOutputStream;
025: import java.io.InputStream;
026: import java.io.IOException;
027: import java.util.ArrayList;
028: import java.util.Collection;
029: import java.util.HashMap;
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Properties;
034: import java.util.StringTokenizer;
035: import java.util.logging.Logger;
036:
037: import javax.jdo.JDOFatalException;
038: import javax.jdo.JDOFatalUserException;
039: import javax.jdo.JDOUserException;
040: import javax.jdo.spi.JDOImplHelper;
041: import javax.jdo.spi.PersistenceCapable;
042: import javax.sql.DataSource;
043:
044: import org.jdom.Document;
045: import org.jdom.Element;
046: import org.jdom.JDOMException;
047: import org.jdom.input.SAXBuilder;
048: import org.xml.sax.EntityResolver;
049: import org.xml.sax.InputSource;
050:
051: import org.xorm.datastore.Column;
052: import org.xorm.datastore.ConnectionInfo;
053: import org.xorm.datastore.Table;
054: import org.xorm.datastore.sql.SQLConnectionInfo;
055: import org.xorm.datastore.sql.SQLType;
056: import org.xorm.util.FieldDescriptor;
057: import org.xorm.util.jdoxml.*;
058:
059: /**
060: * Represents the full set of mappings for an object model.
061: */
062: public class ModelMapping implements Configurable, I15d {
063: protected static Logger logger = Logger
064: .getLogger("org.xorm.ModelMapping");
065:
066: public static final String XORM_VENDOR_NAME = "XORM";
067:
068: public static final String ATTR_SOURCE = "source";
069: public static final String ATTR_TARGET = "target";
070: public static final String ATTR_ORDER_BY = "order-by";
071: public static final String ATTR_INDEX = "index";
072: public static final String ATTR_FILTER = "filter";
073: public static final String ATTR_PARAMETERS = "parameters";
074: public static final String ATTR_VARIABLES = "variables";
075: public static final String ATTR_ORDERING = "ordering";
076: public static final String ATTR_IMPORTS = "imports";
077:
078: public static final String DATABASE_XML = "org.xorm.datastore.database";
079: public static final String DATABASE_DTD = "/org/xorm/datastore/database.dtd";
080:
081: public static final String OPTION_VALIDATE_XML = "org.xorm.option.ValidateXML";
082: public static final String OPTION_BOOTSTRAP_JDO = "org.xorm.option.BootstrapJDO";
083: public static final String OPTION_DEFAULT_MAPPING = "org.xorm.option.DefaultMapping";
084: public static final String OPTION_ONLY_CONFIGURED_PROPERTIES = "org.xorm.option.onlyConfiguredProperties";
085:
086: private HashMap classToMapping = new HashMap();
087: private HashMap nameToTable = new HashMap();
088: private Properties properties;
089: private InterfaceManagerFactory factory;
090: private boolean validateXML = true;
091: private boolean useDefaultMapping = false;
092: private boolean registerClasses = false;
093: private boolean usingDatabaseMetaData = false;
094:
095: public void setFactory(InterfaceManagerFactory factory) {
096: this .factory = factory;
097: }
098:
099: public void addClassMapping(ClassMapping mapping) {
100: Class clazz = mapping.getMappedClass();
101: // System.out.println("Registering class: " + clazz.getName());
102: classToMapping.put(clazz, mapping);
103:
104: if (registerClasses) {
105: // Register XORM-specific classes' JDO Metadata
106:
107: if (!PersistenceCapable.class.isAssignableFrom(clazz)) {
108: InterfaceInvocationHandler handler = new InterfaceInvocationHandler(
109: factory, mapping, null);
110: PersistenceCapable pc = (PersistenceCapable) handler
111: .newProxy();
112:
113: Collection managedFields = mapping.getManagedFields();
114: int len = managedFields.size();
115: String[] fieldNames = new String[len];
116: Class[] fieldTypes = new Class[len];
117: byte[] fieldFlags = new byte[len];
118: int i = 0;
119: Iterator it = managedFields.iterator();
120: while (it.hasNext()) {
121: FieldDescriptor fd = mapping
122: .getFieldDescriptor((String) it.next());
123: fieldNames[i] = fd.name;
124: fieldTypes[i] = fd.type;
125: fieldFlags[i] = (byte) (pc.CHECK_READ | pc.CHECK_WRITE);
126: i++;
127: }
128:
129: // Register it with JDOImplHelper
130: JDOImplHelper.registerClass(clazz, fieldNames,
131: fieldTypes, fieldFlags, null, // PC superclasses not supported
132: pc);
133: }
134: }
135: }
136:
137: /**
138: * Retrieves a ClassMapping for the specified class. If the class
139: * itself is not mapped, but a superclass or superinterface is mapped,
140: * that mapping is cloned and used for the specified class.
141: * @exception JDOUserException if no applicable mapping is found
142: */
143: public ClassMapping getClassMapping(Class clazz) {
144: ClassMapping mapping = getClassMappingImpl(clazz);
145: if (mapping == null) {
146: throw new JDOUserException(I18N.msg("E_no_class_mapping",
147: clazz.getName()));
148: }
149: return mapping;
150: }
151:
152: private ClassMapping getClassMappingImpl(Class clazz) {
153: // First check the cache of previously loaded mappings
154: if (classToMapping.containsKey(clazz)) {
155: return (ClassMapping) classToMapping.get(clazz);
156: }
157: ClassMapping mapping;
158: if ((mapping = loadClassMapping(clazz)) == null) {
159: boolean found = false;
160: Class[] interfaces = clazz.getInterfaces();
161: for (int i = 0; i < interfaces.length; i++) {
162: if ((mapping = getClassMappingImpl(interfaces[i])) != null) {
163: found = true;
164: break;
165: }
166: }
167: if (!found) {
168: /*
169: if(!clazz.isInterface()){
170: //try superclass
171: */
172: Class super class = clazz.getSuperclass();
173: if ((super class != null)
174: && ((mapping = getClassMappingImpl(super class)) != null)) {
175: found = true;
176: }
177: /*
178: } else {
179: //try to find class implementing this iterface, first found will be used
180: logger.info("loking for implementation for interface "+clazz);
181: Iterator classesIter = classToMapping.keySet().iterator();
182: while (classesIter.hasNext()) {
183: Class nextClass = (Class) classesIter.next();
184: if(clazz.isAssignableFrom(nextClass)){
185: logger.info("found matching class "+nextClass);
186: mapping = (ClassMapping)classToMapping.get(nextClass);
187: found = true;
188: break;
189: }
190: }
191: }
192: */
193: }
194: if (found && !mapping.getMappedClass().equals(clazz)) {
195: // Simulate the mapping
196: //System.out.println("Cloning ClassMapping for " + mapping.getMappedClass() + " to " + clazz);
197: mapping = (ClassMapping) mapping.clone();
198: mapping.clazz = clazz;
199: addClassMapping(mapping);
200: //classToMapping.put(clazz, mapping);
201: }
202: }
203: return mapping;
204: }
205:
206: /**
207: * Loads the class mapping information by reading the corresponding
208: * JDO metadata files.
209: *
210: * @param clazz the interface or abstract class class to be loaded
211: * @return a ClassMapping object representing the mappings, or
212: * null if no mapping exists.
213: */
214: private synchronized ClassMapping loadClassMapping(Class clazz) {
215: // First request for this class; read appropriate JDO files.
216: String name = clazz.getName();
217: StringTokenizer toke = new StringTokenizer(name, ".");
218: StringBuffer path = new StringBuffer("/");
219: while (toke.hasMoreTokens()) {
220: path.append(toke.nextToken());
221: if (toke.hasMoreTokens()) {
222: path.append("/");
223: } else {
224: path.append(".jdo");
225: }
226: }
227: InputStream jdo = getClass().getResourceAsStream(
228: path.toString());
229: if (jdo == null) {
230: // Try package file
231: int pos = path.toString().lastIndexOf("/");
232: if (pos > 0) {
233: path.delete(pos, path.length());
234: path.append(".jdo");
235: }
236: jdo = getClass().getResourceAsStream(path.toString());
237: }
238: if (jdo == null) {
239: // No mapping exists
240: classToMapping.put(clazz, null);
241: return null;
242: } else {
243: initJDO(jdo);
244: return (ClassMapping) classToMapping.get(clazz);
245: }
246: }
247:
248: public Table getTable(String name) {
249: return (Table) nameToTable.get(name);
250: }
251:
252: /**
253: * Initializes an empty ModelMapping that will be populated
254: * on demand by reading JDO files, or can be hand-populated
255: * by the user.
256: * @param props the Properties to use
257: */
258: public void setProperties(Properties props) {
259: this .properties = props;
260:
261: validateXML = !"false".equalsIgnoreCase(properties
262: .getProperty(OPTION_VALIDATE_XML));
263: useDefaultMapping = "true".equalsIgnoreCase(properties
264: .getProperty(OPTION_DEFAULT_MAPPING));
265:
266: String databaseXML = properties.getProperty(DATABASE_XML);
267: if (databaseXML != null) {
268: SAXBuilder biff = new SAXBuilder(validateXML);
269: biff.setEntityResolver(new EntityResolver() {
270: public InputSource resolveEntity(String publicId,
271: String systemId) {
272: if ("database.dtd".equals(systemId)) {
273: return new InputSource(getClass()
274: .getResourceAsStream(DATABASE_DTD));
275: }
276: return null;
277: }
278: });
279: InputStream testStream = getClass().getResourceAsStream(
280: databaseXML);
281: if (testStream == null) {
282: testStream = Thread.currentThread()
283: .getContextClassLoader().getResourceAsStream(
284: databaseXML);
285: }
286: if (testStream == null) {
287: throw new JDOFatalUserException(I18N.msg(
288: "E_locate_db_xml", databaseXML));
289: }
290: InputSource inputSource = new InputSource(testStream);
291:
292: // Fake out Crimson
293: inputSource.setSystemId(getClass()
294: .getResource(DATABASE_DTD).toString());
295:
296: try {
297: Document doc = biff.build(inputSource);
298: Element root = doc.getRootElement();
299: parseTables(root);
300: } catch (JDOMException e) {
301: throw new JDOFatalException(I18N.msg("E_parse_db_xml",
302: databaseXML), e);
303: } catch (IOException e) {
304: throw new JDOFatalException(I18N.msg("E_parse_db_xml",
305: databaseXML), e);
306: }
307: } else {
308: // DATABASE_XML was not defined.
309: usingDatabaseMetaData = true;
310: logger
311: .info(DATABASE_XML
312: + " not defined, will dynamically use DatabaseMetaData");
313: }
314:
315: // Check for bootstrap loading of JDO files
316: String bootstrap = properties.getProperty(OPTION_BOOTSTRAP_JDO);
317: if (bootstrap != null) {
318: String[] parts = bootstrap.split(",");
319: registerClasses = true;
320: for (int i = 0; i < parts.length; i++) {
321: InputStream jdo = getClass().getResourceAsStream(
322: parts[i]);
323: initJDO(jdo);
324: }
325: registerClasses = false;
326: }
327: }
328:
329: /**
330: * Returns true if the interface type passed in has been configured
331: * via the mapping file to be persisted.
332: * Note that this is NOT the same thing as checking if an instance
333: * that implements the interface is managed, and in particular, a call to
334: * mgr.isManagedType(persistentInstance.getClass())
335: * will NOT return true.
336: */
337: public boolean isManagedType(Class type) {
338: return (getClassMappingImpl(type) != null);
339: }
340:
341: /** Used as a utility method from init. */
342: private String getPackagedClassName(String className,
343: String defaultPackage) {
344: if (defaultPackage == null || className.indexOf('.') != -1) {
345: return className;
346: }
347: return defaultPackage + '.' + className;
348: }
349:
350: /**
351: * Uses JDOM to load configuration from an XML .jdo file.
352: */
353: private void initJDO(InputStream mappingStream) {
354: try {
355: JDOPackage jdoPackage = JDOXML.read(mappingStream,
356: validateXML);
357: String defaultPackage = jdoPackage.getName();
358: if ("".equals(defaultPackage)) {
359: defaultPackage = null;
360: }
361:
362: if (usingDatabaseMetaData) {
363: // The user didn't specify a DATABASE_XML file, so we need
364: // to load all tables referenced in the .jdo file from the
365: // database.
366: loadReferencedTablesFromMetaData(jdoPackage);
367: }
368:
369: // Look at the classes twice: first create ClassMapping
370: // instances with tables only, then fill in the details.
371: // This is done so that different ClassMappings can use
372: // each other's default fetch group settings, possibly
373: // circularly.
374: Iterator i = jdoPackage.getClasses().iterator();
375: while (i.hasNext()) {
376: JDOClass jdoClass = (JDOClass) i.next();
377: String className = getPackagedClassName(jdoClass
378: .getName(), defaultPackage);
379: Class c = Class.forName(className);
380: ClassMapping classMapping;
381: if ("true"
382: .equalsIgnoreCase(properties
383: .getProperty(OPTION_ONLY_CONFIGURED_PROPERTIES))) {
384: classMapping = new ClassMapping(this , c, jdoClass);
385: } else {
386: classMapping = new ClassMapping(this , c);
387: }
388:
389: Iterator exts = jdoClass.getExtensions().iterator();
390: JDOExtension extension;
391: Class dsi = null;
392: Table t = null;
393: while (exts.hasNext()) {
394: extension = (JDOExtension) exts.next();
395: if (XORM_VENDOR_NAME.equals(extension
396: .getVendorName())) {
397: String key = extension.getKey();
398: if ("datastore-identity-type".equals(key)) {
399: dsi = Class.forName(extension.getValue());
400: } else if ("table".equals(key)) {
401: String tableName = extension.getValue();
402: t = getTable(tableName);
403: if (t == null) {
404: throw new JDOFatalUserException(I18N
405: .msg("E_no_table_definition",
406: tableName));
407: }
408: }
409: }
410: }
411:
412: if (t == null && !useDefaultMapping) {
413: throw new JDOFatalUserException(I18N.msg(
414: "E_no_table", c.getName()));
415: }
416:
417: classMapping.setTable(t);
418: classMapping.setDatastoreIdentityType(dsi);
419: addClassMapping(classMapping);
420: }
421: i = jdoPackage.getClasses().iterator();
422: while (i.hasNext()) {
423: JDOClass jdoClass = (JDOClass) i.next();
424: parseClass(jdoClass, defaultPackage);
425: }
426:
427: } catch (IOException e) {
428: throw new JDOFatalException(I18N.msg("E_parse_jdo_xml"), e);
429: } catch (ClassNotFoundException e) {
430: throw new JDOFatalException(I18N.msg("E_jdo_xml_no_class",
431: e.getMessage()));
432: }
433: }
434:
435: /**
436: * Parses a <class> element passed in as the first parameter.
437: */
438: private void parseClass(JDOClass jdoClass, String defaultPackage)
439: throws ClassNotFoundException {
440: String className = getPackagedClassName(jdoClass.getName(),
441: defaultPackage);
442: Class c = Class.forName(className);
443: ClassMapping classMapping = getClassMapping(c);
444: Table t = classMapping.getTable();
445:
446: Iterator exts;
447: JDOExtension extension;
448: // read fields
449: Iterator j = jdoClass.getFields().iterator();
450: while (j.hasNext()) {
451: JDOField field = (JDOField) j.next();
452: exts = field.getExtensions().iterator();
453: while (exts.hasNext()) {
454: extension = (JDOExtension) exts.next();
455: if (XORM_VENDOR_NAME.equals(extension.getVendorName())) {
456: String key = extension.getKey();
457: if ("column".equals(key)) {
458: Column c2 = t.getColumnByName(extension
459: .getValue());
460: if (c2 == null) {
461: throw new JDOFatalUserException(I18N.msg(
462: "E_no_column",
463: extension.getValue(), t.getName(),
464: jdoClass.getName()));
465: }
466: classMapping.setColumn(field.getName(), c2,
467: field.isDefaultFetchGroup());
468: if (field.getNullValue().equals(
469: JDONullValue.EXCEPTION)) {
470: // TODO: should this be done here?
471: c2.setNonNull(true);
472: }
473: } else if ("inverse".equals(key)) {
474: classMapping.setInverse(field.getName(),
475: extension.getValue());
476: }
477: } // XORM is vendor-name
478: } // end extension elements
479: // See if field is a collection
480: JDOCollection collection = field.getCollection();
481: if (collection != null) {
482: RelationshipMapping rm = parseRelationship(collection,
483: c, defaultPackage, field, jdoClass);
484: classMapping.setRelationship(field.getName(), rm);
485: }
486: }
487: }
488:
489: private RelationshipMapping parseRelationship(JDOCollection root,
490: Class ownerClass, String defaultPackage, JDOField field,
491: JDOClass jdoClass) throws ClassNotFoundException {
492: RelationshipMapping relationship = new RelationshipMapping();
493: String elementType = root.getElementType();
494: elementType = getPackagedClassName(elementType, defaultPackage);
495:
496: Class elementClass = Class.forName(elementType);
497:
498: RelationshipMapping.Endpoint source = new RelationshipMapping.Endpoint();
499: source.setCollectionType(source.SET);
500: source.setElementClass(elementClass);
501: relationship.setSource(source);
502:
503: RelationshipMapping.Endpoint target = new RelationshipMapping.Endpoint();
504: target.setCollectionType(target.SET);
505: relationship.setTarget(target);
506:
507: Table table = null;
508: Iterator i = root.getExtensions().iterator();
509: String sourceStr = null;
510: String targetStr = null;
511: String indexStr = null;
512: // Added support for filtered collections (Dan Checkoway, 6/26/03)
513: String filterStr = null;
514: String parametersStr = null;
515: String variablesStr = null;
516: String importsStr = null;
517: while (i.hasNext()) {
518: JDOExtension element = (JDOExtension) i.next();
519: if (XORM_VENDOR_NAME.equals(element.getVendorName())) {
520: String key = element.getKey();
521: String value = element.getValue();
522: boolean redefined = false;
523: if ("table".equals(key)) {
524: redefined = table != null;
525: table = getTable(value);
526: if (table == null) {
527: throw new JDOFatalUserException(I18N.msg(
528: "E_unknown_table", value));
529: }
530: } else if (ATTR_SOURCE.equals(key)) {
531: redefined = sourceStr != null;
532: sourceStr = value;
533: } else if (ATTR_TARGET.equals(key)) {
534: redefined = targetStr != null;
535: targetStr = value;
536: } else if (ATTR_ORDER_BY.equals(key)) {
537: redefined = relationship.getOrderBy() != null;
538: relationship.setOrderBy(value);
539: } else if (ATTR_INDEX.equals(key)) {
540: redefined = indexStr != null;
541: indexStr = value;
542: } else if (ATTR_FILTER.equals(key)) {
543: // Filtered collection query
544: redefined = filterStr != null;
545: filterStr = value;
546: } else if (ATTR_PARAMETERS.equals(key)) {
547: // Filtered collection query parameters
548: redefined = parametersStr != null;
549: parametersStr = value;
550: } else if (ATTR_VARIABLES.equals(key)) {
551: // Filtered collection query variables
552: redefined = variablesStr != null;
553: variablesStr = value;
554: } else if (ATTR_ORDERING.equals(key)) {
555: // JDO-style ordering
556: redefined = relationship.getOrdering() != null;
557: relationship.setOrdering(value);
558: } else if (ATTR_IMPORTS.equals(key)) {
559: redefined = importsStr != null;
560: importsStr = value;
561: }
562: if (redefined) {
563: throw new JDOFatalUserException(I18N.msg(
564: "E_collection_redefinition", key, field
565: .getName(), jdoClass.getName()));
566: }
567: }
568: } // for each "extension" element
569:
570: if (sourceStr == null && filterStr == null) {
571: if (useDefaultMapping) {
572: sourceStr = "source_" + ownerClass.getName();
573: } else {
574: // You have to specify either a source or a filter or both.
575: throw new JDOFatalUserException(I18N.msg(
576: "E_collection_no_source", field.getName(),
577: jdoClass.getName()));
578: }
579: }
580:
581: if (table == null) {
582: // The table wasn't explicitly specified. We can safely
583: // assume that if a collection is specified without an explicit
584: // table, then the table is the collection element type's table.
585: ClassMapping mapping = getClassMapping(elementClass);
586: if ((table = mapping.getTable()) == null) {
587: // Not much we can do about this. I don't think it will ever
588: // happen, but just in case...
589: throw new JDOFatalUserException(I18N.msg(
590: "E_collection_no_table", field.getName(),
591: jdoClass.getName()));
592: }
593: logger.fine("No table specified for collection field \""
594: + field.getName()
595: + "\"...assuming element type table: "
596: + table.getName());
597: }
598:
599: if (relationship.getOrderBy() != null
600: && relationship.getOrdering() != null) {
601: // Don't even bother trying to interpret precedence
602: throw new JDOFatalUserException(I18N
603: .msg("E_collection_order_by_and_ordering"));
604: }
605:
606: if (relationship.getOrdering() != null && filterStr == null) {
607: throw new JDOFatalUserException(I18N.msg(
608: "E_collection_ordering_without_filter", field
609: .getName(), jdoClass.getName()));
610: }
611:
612: if (sourceStr != null) {
613: source.setColumn(table.getColumnByName(sourceStr));
614: }
615:
616: if (targetStr == null) {
617: target.setColumn(table.getPrimaryKey());
618: } else {
619: // It's many-to-many. We used to set the target's elementClass
620: // to signify this, but that seemed like a hack to me. And since
621: // the target's elementClass was set to the owner class, which
622: // didn't seem to apply (if anything the target element class
623: // would be the collection's element class, not the owner's class),
624: // I added this boolean setter/getter instead.
625: relationship.setMToN(true);
626: target.setColumn(table.getColumnByName(targetStr));
627: }
628:
629: if (indexStr != null) {
630: Column c = table.getColumnByName(indexStr);
631: // Set the column to managed mode; we don't want
632: // indices to be included in Row.equals() operations
633: c.setManaged(true);
634: relationship.setIndexColumn(c);
635: }
636:
637: if (filterStr != null && !filterStr.equals("")) {
638: relationship.setFilter(filterStr);
639: // Only set parameters & variables if the filter is specified.
640: // Otherwise it would make no sense.
641: if (parametersStr != null && !parametersStr.equals("")) {
642: relationship.setParameters(parametersStr);
643: }
644: if (variablesStr != null && !variablesStr.equals("")) {
645: relationship.setVariables(variablesStr);
646: }
647: if (importsStr != null && !importsStr.equals("")) {
648: relationship.setImports(importsStr);
649: }
650: }
651:
652: return relationship;
653: }
654:
655: private void parseTables(Element root) {
656: List tables = root.getChildren("table");
657: Iterator i = tables.iterator();
658: while (i.hasNext()) {
659: Element element = (Element) i.next();
660: String tableName = element.getAttributeValue("name");
661: Table table = new Table(tableName);
662: nameToTable.put(tableName, table);
663: List columns = element.getChildren("column");
664: Iterator j = columns.iterator();
665: while (j.hasNext()) {
666: Element colElement = (Element) j.next();
667: Column column = new Column(table, colElement
668: .getAttributeValue("name"));
669: if ("true".equalsIgnoreCase(colElement
670: .getAttributeValue("primary-key"))) {
671: table.setPrimaryKey(column);
672: }
673: if ("true".equalsIgnoreCase(colElement
674: .getAttributeValue("read-only"))) {
675: column.setReadOnly(true);
676: }
677: if ("true".equalsIgnoreCase(colElement
678: .getAttributeValue("non-null"))) {
679: column.setNonNull(true);
680: }
681: // Is it a sequenced column?
682: if (colElement.getAttributeValue("sequence") != null) {
683: column.setSequence(colElement
684: .getAttributeValue("sequence"));
685: }
686: // Is it an autoincremented column?
687: if ("true".equalsIgnoreCase(colElement
688: .getAttributeValue("auto"))) {
689: column.setAutoIncremented(true);
690: }
691: // Is it manually typed?
692: column.setType(colElement.getAttributeValue("type"));
693: column
694: .setFormat(colElement
695: .getAttributeValue("format"));
696: } // columns iterator
697: // map the table for later reference
698: } // tables iterator
699: }
700:
701: /**
702: * Generates a list of table names extracted from the JDO file
703: * and passes each one to ConnectionInfo.describeTable(String name).
704: */
705: private void loadReferencedTablesFromMetaData(JDOPackage jdoPackage) {
706: HashSet tableNames = new HashSet();
707: for (Iterator classIter = jdoPackage.getClasses().iterator(); classIter
708: .hasNext();) {
709: JDOClass jdoClass = (JDOClass) classIter.next();
710: // Look at the class extensions for the "table"
711: for (Iterator extIter = jdoClass.getExtensions().iterator(); extIter
712: .hasNext();) {
713: JDOExtension extension = (JDOExtension) extIter.next();
714: if ("table".equals(extension.getKey())) {
715: String tableName = extension.getValue();
716: if (tableNames.add(tableName)) {
717: logger.fine("Discovered table name: "
718: + tableName);
719: }
720: }
721: }
722: // Look at the class fields and everything under there
723: for (Iterator fieldIter = jdoClass.getFields().iterator(); fieldIter
724: .hasNext();) {
725: JDOField field = (JDOField) fieldIter.next();
726: // Look at the field's extensions
727: for (Iterator extIter = field.getExtensions()
728: .iterator(); extIter.hasNext();) {
729: JDOExtension extension = (JDOExtension) extIter
730: .next();
731: if ("table".equals(extension.getKey())) {
732: String tableName = extension.getValue();
733: if (tableNames.add(tableName)) {
734: logger.fine("Discovered table name: "
735: + tableName);
736: }
737: }
738: }
739:
740: // Look at the field's collection, if there is one
741: JDOCollection collection = field.getCollection();
742: if (collection != null) {
743: for (Iterator extIter = collection.getExtensions()
744: .iterator(); extIter.hasNext();) {
745: JDOExtension extension = (JDOExtension) extIter
746: .next();
747: if ("table".equals(extension.getKey())) {
748: String tableName = extension.getValue();
749: if (tableNames.add(tableName)) {
750: logger.fine("Discovered table name: "
751: + tableName);
752: }
753: }
754: }
755: }
756: }
757: }
758:
759: logger.fine("Discovered " + tableNames.size()
760: + " table names...loading them now");
761:
762: ConnectionInfo ci = factory.getConnectionInfo();
763: for (Iterator iter = tableNames.iterator(); iter.hasNext();) {
764: String tableName = (String) iter.next();
765: // Load it from db metadata
766: nameToTable.put(tableName, ci.describeTable(tableName));
767: }
768: }
769: }
|