0001: /*
0002: * Copyright 2006 Le Duc Bao, Ralf Joachim
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License. You may obtain a copy of
0006: * the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013: * License for the specific language governing permissions and limitations under
0014: * the License.
0015: */
0016: package org.castor.ddlgen;
0017:
0018: import java.io.OutputStream;
0019: import java.util.Enumeration;
0020: import java.util.HashMap;
0021: import java.util.Iterator;
0022: import java.util.Map;
0023:
0024: import org.castor.ddlgen.schemaobject.Field;
0025: import org.castor.ddlgen.schemaobject.ForeignKey;
0026: import org.castor.ddlgen.schemaobject.KeyGenerator;
0027: import org.castor.ddlgen.schemaobject.PrimaryKey;
0028: import org.castor.ddlgen.schemaobject.Schema;
0029: import org.castor.ddlgen.schemaobject.Table;
0030: import org.castor.ddlgen.typeinfo.TypeInfo;
0031: import org.exolab.castor.mapping.Mapping;
0032: import org.exolab.castor.mapping.xml.ClassChoice;
0033: import org.exolab.castor.mapping.xml.ClassMapping;
0034: import org.exolab.castor.mapping.xml.FieldMapping;
0035: import org.exolab.castor.mapping.xml.KeyGeneratorDef;
0036: import org.exolab.castor.mapping.xml.MapTo;
0037: import org.exolab.castor.mapping.xml.MappingRoot;
0038: import org.exolab.castor.mapping.xml.Sql;
0039:
0040: /**
0041: * AbstractGenerator is the base class for various DDL generator of specific DB and
0042: * handles following tasks:
0043: * <li/> Extract information from Mapping to Schema
0044: * <li/> Loop through the schema and provide a skeleton for DDL creation
0045: *
0046: * <p/>AbstractGenerator will automatically extract necessary information for DDL
0047: * creation. That information is handled by Schema.
0048: * <p/>To create new generator for a DBMS, you should:
0049: * <li/> Overwrite this class to create new generator for a DBMS.
0050: * <li/> If the syntax of DBMS is different to standard DDL syntax, you should
0051: * overwrite SchemaObject (Table, Field, KeyGenerator, Index, ForeignKey,...) classes.
0052: * The class SchemaObjectFactory who handles the SchemaObject creation must
0053: * be overwritten.
0054: * <li/> You must overwrite the TypeMapper if mapping between JDBC types and
0055: * specific DBMS’s types is different among various DBMS.
0056: * <p/>The example bellow shows how to create a generator for DB2:
0057: * <li/> <b>Generator for DB2</b>
0058: * <pre>
0059: *public class Db2Generator extends AbstractGenerator {
0060: *
0061: * public Db2Generator(final String globConf, final String dbConf)
0062: * throws GeneratorException {
0063: * super(globConf, dbConf);
0064: * setTypeMapper(new Db2TypeMapper(getConf()));
0065: * }
0066: *}
0067: * </pre>
0068: * <li/><b>TypeMapper for DB2</b>
0069: * <pre>
0070: *public final class Db2TypeMapper extends AbstractTypeMapper {
0071: * public Db2TypeMapper(final Configuration conf) {
0072: * super(conf);
0073: * }
0074: *
0075: * protected void initialize(final Configuration conf) {
0076: * // numeric types
0077: * this.add(new NotSupportedType("bit"));
0078: * LOG.warn("Db2 does not support 'TINY' type, use SMALLINT instead.");
0079: * this.add(new NoParamType("tinyint", "SMALLINT"));
0080: * this.add(new NoParamType("smallint", "SMALLINT"));
0081: * this.add(new NoParamType("integer", "INTEGER"));
0082: * this.add(new NoParamType("bigint", "BIGINT"));
0083: * }
0084: *}
0085: *</pre>
0086: * <li/><b>Field for DB2</b>
0087: *<pre>
0088: *public class Db2Field extends Field {
0089: * public Db2Field() {
0090: * super();
0091: * }
0092: *
0093: * public String toDDL() throws GeneratorException {
0094: * StringBuffer buff = new StringBuffer();
0095: * buff.append(getName()).append(" ");
0096: * buff.append(getType().toDDL(this));
0097: *
0098: * if (isIdentity()) {
0099: * buff.append(" NOT NULL");
0100: * }
0101: *
0102: * KeyGenerator keyGen = getKeyGenerator();
0103: * if (keyGen != null && isIdentity()) {
0104: *
0105: * if (KeyGenerator.IDENTITY_KEY.equalsIgnoreCase(keyGen.getName())) {
0106: * buff.append(" GENERATED BY DEFAULT AS IDENTITY ").
0107: * append("START WITH 1 INCREMENT BY 1");
0108: * }
0109: * }
0110: *
0111: * return buff.toString();
0112: * }
0113: *}
0114: *</pre>
0115: * <li/><b>Field for DB2</b>
0116: *<pre>
0117: *public class Db2SchemaFactory extends SchemaFactory {
0118: * public Db2SchemaFactory() {
0119: * super();
0120: * }
0121: * public Field createField() {
0122: * return new Db2Field();
0123: * }
0124: *
0125: *}
0126: *</pre>
0127: * The GeneratorFactory class handles the specific database generator creation.
0128: * For example:
0129: * <pre>
0130: * Generator generator = GeneratorFactory.
0131: * createDDLGenerator(“mysql”, “ddl.properties”, “mysql.properties”);
0132: * </pre>
0133: *
0134: * And to generate DDL, it should specify the printer and call generateDDL method.
0135: * <pre>
0136: * generator.setPrinter(System.out);
0137: * Mapping mapping = new Mapping();
0138: * mapping.loadMapping("mapping.xml");
0139: * generator.generateDDL(mapping);
0140: * </pre>
0141: *
0142: * @author <a href="mailto:leducbao AT gmail DOT com">Le Duc Bao</a>
0143: * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
0144: * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
0145: * @since 1.1
0146: */
0147: public abstract class AbstractGenerator implements Generator {
0148: //--------------------------------------------------------------------------
0149:
0150: /** handle all configurations (key, value). */
0151: private final DDLGenConfiguration _configuration;
0152:
0153: /** handle the key gen registry. */
0154: private KeyGeneratorRegistry _keyGenRegistry;
0155:
0156: /** handle the MappingHelper. */
0157: private MappingHelper _mappingHelper;
0158:
0159: /** handle the typemapper. */
0160: private TypeMapper _typeMapper;
0161:
0162: /** handle schema factory. */
0163: private SchemaFactory _schemaFactory;
0164:
0165: /** handle the _mapping document. */
0166: private Mapping _mapping;
0167:
0168: /** schema. */
0169: private Schema _schema;
0170:
0171: /** handle all resolving tables. */
0172: private final Map _resolveTable = new HashMap();
0173:
0174: //--------------------------------------------------------------------------
0175:
0176: /**
0177: * Constructor for AbstractGenerator.
0178: *
0179: * @param configuration Configuration to use by the generator.
0180: */
0181: protected AbstractGenerator(final DDLGenConfiguration configuration) {
0182: _configuration = configuration;
0183: }
0184:
0185: //--------------------------------------------------------------------------
0186:
0187: /**
0188: * Get configuration of generator.
0189: *
0190: * @return Configuration of generator.
0191: */
0192: public final DDLGenConfiguration getConfiguration() {
0193: return _configuration;
0194: }
0195:
0196: /**
0197: * Set key generator registry.
0198: *
0199: * @param keyGenRegistry Key generator registry.
0200: */
0201: public final void setKeyGenRegistry(
0202: final KeyGeneratorRegistry keyGenRegistry) {
0203: _keyGenRegistry = keyGenRegistry;
0204: }
0205:
0206: /**
0207: * Set mapping helper.
0208: *
0209: * @param mappingHelper Mapping helper.
0210: */
0211: protected final void setMappingHelper(
0212: final MappingHelper mappingHelper) {
0213: _mappingHelper = mappingHelper;
0214: _mappingHelper.setTypeMapper(_typeMapper);
0215: }
0216:
0217: /**
0218: * Get mapping helper.
0219: *
0220: * @return Mapping helper.
0221: */
0222: public final MappingHelper getMappingHelper() {
0223: return _mappingHelper;
0224: }
0225:
0226: /**
0227: * Set type mapper.
0228: *
0229: * @param typeMapper Type mapper.
0230: */
0231: public final void setTypeMapper(final TypeMapper typeMapper) {
0232: _typeMapper = typeMapper;
0233: _mappingHelper.setTypeMapper(_typeMapper);
0234: }
0235:
0236: /**
0237: * Get type mapper.
0238: *
0239: * @return Type mapper.
0240: */
0241: public final TypeMapper getTypeMapper() {
0242: return _typeMapper;
0243: }
0244:
0245: /**
0246: * Set schema factory.
0247: *
0248: * @param schemaFactory Schema factory.
0249: */
0250: protected final void setSchemaFactory(
0251: final SchemaFactory schemaFactory) {
0252: _schemaFactory = schemaFactory;
0253: }
0254:
0255: /**
0256: * Get schema factory.
0257: *
0258: * @return Schema factory.
0259: */
0260: public final SchemaFactory getSchemaFactory() {
0261: return _schemaFactory;
0262: }
0263:
0264: /**
0265: * Set mapping document.
0266: *
0267: * @param mapping Mapping document.
0268: */
0269: public final void setMapping(final Mapping mapping) {
0270: _mapping = mapping;
0271: _mappingHelper.setMapping(_mapping);
0272: }
0273:
0274: /**
0275: * Get mapping document.
0276: *
0277: * @return Mapping document.
0278: */
0279: public final Mapping getMapping() {
0280: return _mapping;
0281: }
0282:
0283: /**
0284: * Get schema.
0285: *
0286: * @return Schema
0287: */
0288: public final Schema getSchema() {
0289: return _schema;
0290: }
0291:
0292: //--------------------------------------------------------------------------
0293:
0294: /**
0295: * {@inheritDoc}
0296: */
0297: public final void generateDDL(final OutputStream output)
0298: throws GeneratorException {
0299: DDLWriter writer = new DDLWriter(output, _configuration);
0300:
0301: // Create schema.
0302: createSchema();
0303:
0304: // Generate DDL.
0305: String groupBy = _configuration.getStringValue(
0306: DDLGenConfiguration.GROUP_DDL_KEY,
0307: DDLGenConfiguration.GROUP_DDL_BY_TABLE);
0308: if (DDLGenConfiguration.GROUP_DDL_BY_TABLE
0309: .equalsIgnoreCase(groupBy)) {
0310: generateDDLGroupByTable(writer);
0311: } else if (DDLGenConfiguration.GROUP_DDL_BY_DDLTYPE
0312: .equalsIgnoreCase(groupBy)) {
0313: generateDDLGroupByDDLType(writer);
0314: } else {
0315: throw new GeneratorException(
0316: "group ddl by do not support: " + groupBy);
0317: }
0318:
0319: writer.close();
0320: }
0321:
0322: /**
0323: * Generating ddl grouped by ddl type of DDL (e.g DROP, CREATE TABLE, create
0324: * Primary key, create foreign key).
0325: *
0326: * @param writer DDLWriter to write schema objects to.
0327: * @throws GeneratorException If failed to generate DDL.
0328: */
0329: private void generateDDLGroupByDDLType(final DDLWriter writer)
0330: throws GeneratorException {
0331: boolean genSchema = _configuration.getBoolValue(
0332: DDLGenConfiguration.GENERATE_DDL_FOR_SCHEMA_KEY, true);
0333: boolean genDrop = _configuration.getBoolValue(
0334: DDLGenConfiguration.GENERATE_DDL_FOR_DROP_KEY, true);
0335: boolean genCreate = _configuration.getBoolValue(
0336: DDLGenConfiguration.GENERATE_DDL_FOR_CREATE_KEY, true);
0337: boolean genPrimaryKey = _configuration.getBoolValue(
0338: DDLGenConfiguration.GENERATE_DDL_FOR_PRIMARYKEY_KEY,
0339: true);
0340: boolean genForeignKey = _configuration.getBoolValue(
0341: DDLGenConfiguration.GENERATE_DDL_FOR_FOREIRNKEY_KEY,
0342: true);
0343: boolean genIndex = _configuration.getBoolValue(
0344: DDLGenConfiguration.GENERATE_DDL_FOR_INDEX_KEY, true);
0345: boolean genKeyGen = _configuration.getBoolValue(
0346: DDLGenConfiguration.GENERATE_DDL_FOR_KEYGENERATOR_KEY,
0347: true);
0348:
0349: generateHeader(writer);
0350:
0351: //generate ddl for schema
0352: if (genSchema) {
0353: _schema.toCreateDDL(writer);
0354: }
0355:
0356: //generate drop statemetn
0357: if (genDrop) {
0358: generateDrop(writer);
0359: }
0360:
0361: //generate create statement
0362: if (genCreate) {
0363: generateCreate(writer);
0364: }
0365:
0366: //generate primary key creation statement
0367: if (genPrimaryKey) {
0368: generatePrimaryKey(writer);
0369: }
0370:
0371: //generate foreign key creation statement
0372: if (genForeignKey) {
0373: generateForeignKey(writer);
0374: }
0375:
0376: //generate index creation statement
0377: if (genIndex) {
0378: generateIndex(writer);
0379: }
0380:
0381: if (genKeyGen) {
0382: generateKeyGenerator(writer);
0383: }
0384: }
0385:
0386: /**
0387: * Generate DDL for drop statement of table.
0388: *
0389: * @param writer DDLWriter to write schema objects to.
0390: * @throws GeneratorException If failed to generate DDL.
0391: */
0392: public final void generateDrop(final DDLWriter writer)
0393: throws GeneratorException {
0394: for (int i = 0; i < _schema.getTableCount(); i++) {
0395: _schema.getTable(i).toDropDDL(writer);
0396: }
0397: }
0398:
0399: /**
0400: * Generate DDL for create statementof table.
0401: * <pre>
0402: * CREATE TABLE prod (
0403: * id INTEGER NOT NULL,
0404: * name CHAR(16)
0405: * );
0406: *
0407: * CREATE TABLE prod_detail (
0408: * id INTEGER NOT NULL,
0409: * prod_id CHAR(16)
0410: * );
0411: * </pre>
0412: *
0413: * @param writer DDLWriter to write schema objects to.
0414: * @throws GeneratorException If failed to generate DDL.
0415: */
0416: public final void generateCreate(final DDLWriter writer)
0417: throws GeneratorException {
0418: for (int i = 0; i < _schema.getTableCount(); i++) {
0419: _schema.getTable(i).toCreateDDL(writer);
0420: }
0421: }
0422:
0423: /**
0424: * Generate DDL for primany keys.
0425: *
0426: * @param writer DDLWriter to write schema objects to.
0427: * @throws GeneratorException If failed to generate DDL.
0428: */
0429: public final void generatePrimaryKey(final DDLWriter writer)
0430: throws GeneratorException {
0431: for (int i = 0; i < _schema.getTableCount(); i++) {
0432: _schema.getTable(i).getPrimaryKey().toCreateDDL(writer);
0433: }
0434: }
0435:
0436: /**
0437: * Generate DDL for foreign keys.
0438: * <pre>
0439: * ALTER TABLE `prod_group` ADD CONSTRAINT `FK_prod_group_1`
0440: * FOREIGN KEY `FK_prod_group_1` (`id`, `name`)
0441: * REFERENCES `category` (`id`, `name`)
0442: * ON DELETE SET NULL
0443: * ON UPDATE CASCADE;
0444: * </pre>
0445: *
0446: * @param writer DDLWriter to write schema objects to.
0447: * @throws GeneratorException If failed to generate DDL.
0448: */
0449: public final void generateForeignKey(final DDLWriter writer)
0450: throws GeneratorException {
0451: for (int i = 0; i < _schema.getTableCount(); i++) {
0452: createForeignKeyDDL(_schema.getTable(i), writer);
0453: }
0454: }
0455:
0456: /**
0457: * Generate DDL for indices.
0458: *
0459: * @param writer DDLWriter to write schema objects to.
0460: * @throws GeneratorException If failed to generate DDL.
0461: */
0462: public final void generateIndex(final DDLWriter writer)
0463: throws GeneratorException {
0464: for (int i = 0; i < _schema.getTableCount(); i++) {
0465: createIndex(_schema.getTable(i), writer);
0466: }
0467: }
0468:
0469: /**
0470: * Generate DDL for key generators (sequence/trigger).
0471: *
0472: * @param writer DDLWriter to write schema objects to.
0473: * @throws GeneratorException If failed to generate DDL.
0474: */
0475: public final void generateKeyGenerator(final DDLWriter writer)
0476: throws GeneratorException {
0477: for (int i = 0; i < _schema.getTableCount(); i++) {
0478: Table table = _schema.getTable(i);
0479: if (table.getKeyGenerator() != null) {
0480: table.getKeyGenerator().setTable(table);
0481: table.getKeyGenerator().toCreateDDL(writer);
0482: }
0483: }
0484: }
0485:
0486: /**
0487: * Generating ddl group by table.
0488: *
0489: * @param writer DDLWriter to write schema objects to.
0490: * @throws GeneratorException If failed to generate DDL.
0491: */
0492: private void generateDDLGroupByTable(final DDLWriter writer)
0493: throws GeneratorException {
0494: boolean genSchema = _configuration.getBoolValue(
0495: DDLGenConfiguration.GENERATE_DDL_FOR_SCHEMA_KEY, true);
0496: boolean genDrop = _configuration.getBoolValue(
0497: DDLGenConfiguration.GENERATE_DDL_FOR_DROP_KEY, true);
0498: boolean genCreate = _configuration.getBoolValue(
0499: DDLGenConfiguration.GENERATE_DDL_FOR_CREATE_KEY, true);
0500: boolean genPrimaryKey = _configuration.getBoolValue(
0501: DDLGenConfiguration.GENERATE_DDL_FOR_PRIMARYKEY_KEY,
0502: true);
0503: boolean genForeignKey = _configuration.getBoolValue(
0504: DDLGenConfiguration.GENERATE_DDL_FOR_FOREIRNKEY_KEY,
0505: true);
0506: boolean genIndex = _configuration.getBoolValue(
0507: DDLGenConfiguration.GENERATE_DDL_FOR_INDEX_KEY, true);
0508: boolean genKeyGen = _configuration.getBoolValue(
0509: DDLGenConfiguration.GENERATE_DDL_FOR_KEYGENERATOR_KEY,
0510: true);
0511:
0512: generateHeader(writer);
0513:
0514: if (genSchema) {
0515: _schema.toCreateDDL(writer);
0516: }
0517:
0518: for (int i = 0; i < _schema.getTableCount(); i++) {
0519: Table table = _schema.getTable(i);
0520:
0521: if (genDrop) {
0522: table.toDropDDL(writer);
0523: }
0524: if (genCreate) {
0525: table.toCreateDDL(writer);
0526: }
0527: if (genPrimaryKey) {
0528: table.getPrimaryKey().toCreateDDL(writer);
0529: }
0530: if (genForeignKey) {
0531: createForeignKeyDDL(table, writer);
0532: }
0533: if (genIndex) {
0534: createIndex(table, writer);
0535: }
0536: if (genKeyGen && (table.getKeyGenerator() != null)) {
0537: table.getKeyGenerator().setTable(table);
0538: table.getKeyGenerator().toCreateDDL(writer);
0539: }
0540: }
0541: }
0542:
0543: /**
0544: * Generate DDL for foreign key.
0545: *
0546: * @param table Table to generate DDL of foreign key for.
0547: * @param writer DDLWriter to write schema objects to.
0548: * @throws GeneratorException If failed to generate DDL.
0549: */
0550: protected final void createForeignKeyDDL(final Table table,
0551: final DDLWriter writer) throws GeneratorException {
0552: for (int i = 0; i < table.getForeignKeyCount(); i++) {
0553: table.getForeignKey(i).toCreateDDL(writer);
0554: }
0555: }
0556:
0557: /**
0558: * Generate DDL for indices of given table.
0559: *
0560: * @param table Table to generate DDL of indices for.
0561: * @param writer DDLWriter to write schema objects to.
0562: * @throws GeneratorException If failed to generate DDL.
0563: */
0564: public final void createIndex(final Table table,
0565: final DDLWriter writer) throws GeneratorException {
0566: for (int i = 0; i < table.getIndexCount(); i++) {
0567: table.getIndex(i).toCreateDDL(writer);
0568: }
0569: }
0570:
0571: //--------------------------------------------------------------------------
0572:
0573: /**
0574: * Generate header comment.
0575: *
0576: * @param writer DDLWriter to write schema objects to.
0577: */
0578: public abstract void generateHeader(final DDLWriter writer);
0579:
0580: //--------------------------------------------------------------------------
0581:
0582: /**
0583: * Extracting informations from mapping to schema, this is done by 3 steps.
0584: * <ul>
0585: * <li>Create key generators</li>
0586: * <li>Create tables</li>
0587: * <li>Create additional tables for many-many relations</li>
0588: * </ul>
0589: *
0590: * @throws GeneratorException If failed to create schema objects.
0591: */
0592: public final void createSchema() throws GeneratorException {
0593: // Create schema.
0594: MappingRoot root = _mapping.getRoot();
0595: _schema = _schemaFactory.createSchema();
0596: _schema.setConfiguration(_configuration);
0597:
0598: // Create key generators.
0599: Enumeration ekg = root.enumerateKeyGeneratorDef();
0600: while (ekg.hasMoreElements()) {
0601: KeyGeneratorDef definition = (KeyGeneratorDef) ekg
0602: .nextElement();
0603: _keyGenRegistry.createKeyGenerator(definition);
0604: }
0605:
0606: // Create tables.
0607: Enumeration ec = root.enumerateClassMapping();
0608: while (ec.hasMoreElements()) {
0609: ClassMapping cm = (ClassMapping) ec.nextElement();
0610: Table table = createTable(cm);
0611: if (table != null) {
0612: _schema.addTable(table);
0613: }
0614: }
0615:
0616: // Create N:M relation tables.
0617: Iterator i = _resolveTable.keySet().iterator();
0618: while (i.hasNext()) {
0619: ClassMapping cm = (ClassMapping) _resolveTable
0620: .get(i.next());
0621: Table table = createTable(cm);
0622: if (table != null) {
0623: _schema.addTable(table);
0624: }
0625: }
0626: }
0627:
0628: /**
0629: * Create table from a ClassMapping.
0630: *
0631: * @param cm ClassMapping.
0632: * @return Table schema object.
0633: * @throws GeneratorException If failed to create schema objects.
0634: */
0635: private Table createTable(final ClassMapping cm)
0636: throws GeneratorException {
0637: String tableName = cm.getMapTo().getTable();
0638: if (tableName == null) {
0639: return null;
0640: }
0641:
0642: Table table = _schemaFactory.createTable();
0643: table.setName(tableName);
0644: table.setConfiguration(_configuration);
0645: table.setSchema(_schema);
0646:
0647: PrimaryKey primarykey = _schemaFactory.createPrimaryKey();
0648: primarykey.setConfiguration(_configuration);
0649: primarykey.setTable(table);
0650: primarykey.setName("pk_" + tableName);
0651: table.setPrimaryKey(primarykey);
0652:
0653: // Return if there are no field in the table.
0654: if (cm.getClassChoice() == null) {
0655: return table;
0656: }
0657:
0658: boolean isUseFieldIdentity = _mappingHelper
0659: .isUseFieldIdentity(cm);
0660: Enumeration ef = cm.getClassChoice().enumerateFieldMapping();
0661:
0662: // Process key generator.
0663: String keygenerator = cm.getKeyGenerator();
0664: KeyGenerator keyGen = null;
0665: if (keygenerator != null) {
0666: keyGen = _keyGenRegistry.getKeyGenerator(keygenerator
0667: .toUpperCase());
0668: }
0669: table.setKeyGenerator(keyGen);
0670:
0671: while (ef.hasMoreElements()) {
0672: FieldMapping fm = (FieldMapping) ef.nextElement();
0673:
0674: // Skip if <sql> tag is not defined and we have no mapping to DB.
0675: if (fm.getSql() == null) {
0676: continue;
0677: }
0678:
0679: boolean isFieldIdentity = fm.getIdentity();
0680: if (!isUseFieldIdentity) {
0681: isFieldIdentity = _mappingHelper.isIdentity(cm, fm);
0682: }
0683:
0684: // Checke for many-key, many-table definition.
0685: if (fm.getSql().getManyTable() != null) {
0686: // Generate resolving table for many-many relationship
0687: addResolveField(fm, cm);
0688: }
0689:
0690: // Process column creation if sql name is defined.
0691: String[] sqlnames = fm.getSql().getName();
0692: if ((sqlnames != null) && (sqlnames.length > 0)
0693: && (fm.getSql().getManyTable() == null)) {
0694: // Normal case, using sql name as column name.
0695: String sqltype = fm.getSql().getType();
0696:
0697: TypeInfo typeInfo = null;
0698: ClassMapping cmRef = null;
0699: String[] refIdTypes = null;
0700: boolean isUseReferenceType = false;
0701:
0702: // Get type info.
0703: if (sqltype != null) {
0704: typeInfo = _typeMapper.getType(sqltype);
0705: }
0706:
0707: // If typeInfo is null, this table has a reference to another one.
0708: if (typeInfo == null) {
0709: cmRef = _mappingHelper.getClassMappingByName(fm
0710: .getType());
0711: // Use field type if reference class could not be found.
0712: if (cmRef == null) {
0713: typeInfo = _typeMapper.getType(fm.getType());
0714:
0715: if (typeInfo == null) {
0716: throw new TypeNotFoundException(
0717: "can not resolve type "
0718: + fm.getType()
0719: + " in class '"
0720: + cm.getName() + "'");
0721: }
0722: } else {
0723: isUseReferenceType = true;
0724: refIdTypes = _mappingHelper
0725: .resolveTypeReferenceForIds(fm
0726: .getType());
0727:
0728: // If number of reference table's Id's differ from number of
0729: // field elements.
0730: if (refIdTypes.length != sqlnames.length) {
0731: throw new TypeNotFoundException(
0732: "number of reference table's Id differs"
0733: + " to number of field elements '"
0734: + fm.getName()
0735: + "' of class '"
0736: + cm.getName() + "'"
0737: + refIdTypes.length + ","
0738: + sqlnames.length);
0739: }
0740: }
0741: }
0742:
0743: // Create fields.
0744: for (int i = 0; i < sqlnames.length; i++) {
0745: Field field = _schemaFactory.createField();
0746: field.setConfiguration(_configuration);
0747:
0748: if (isUseReferenceType) {
0749: // Each sqlname correspond to a identity of the reference table.
0750: // Should be able to get the original type of the reference
0751: // field.
0752: typeInfo = _typeMapper.getType(refIdTypes[i]);
0753: if (typeInfo == null) {
0754: throw new TypeNotFoundException(
0755: "can not find reference type "
0756: + refIdTypes[i]
0757: + " of class "
0758: + cm.getName());
0759: }
0760: }
0761:
0762: // process attributes of field
0763: field.setName(sqlnames[i]);
0764: field.setTable(table);
0765: field.setType(typeInfo);
0766: field.setIdentity(isFieldIdentity);
0767: field.setRequired(fm.getRequired());
0768: field.setKeyGenerator(keyGen);
0769:
0770: table.addField(field);
0771:
0772: if (isFieldIdentity) {
0773: primarykey.addField(field);
0774: }
0775: }
0776:
0777: // Create foreign keys.
0778: if (isUseReferenceType) {
0779: addOneOneForeignKey(table, fm);
0780: }
0781: }
0782: }
0783:
0784: // Process extends, if extends is defined.
0785: processExtendedClass(table, cm);
0786:
0787: return table;
0788: }
0789:
0790: /**
0791: * Extract identities from extended ClassMapping and add them to table.
0792: *
0793: * @param table Table to add identities of extended mapping to.
0794: * @param cm ClassMapping of table to extract get mappings from.
0795: * @throws GeneratorException throw exception if key-gen is not found.
0796: */
0797: private void processExtendedClass(final Table table,
0798: final ClassMapping cm) throws GeneratorException {
0799: Object extendClass = cm.getExtends();
0800: if (extendClass == null) {
0801: return;
0802: }
0803:
0804: ClassMapping extendCm = (ClassMapping) extendClass;
0805: String[] childIds = _mappingHelper.getClassMappingSqlIdentity(
0806: cm, false);
0807:
0808: if (childIds.length != 0) {
0809: // Check consistency.
0810: String[] childTypes = _mappingHelper
0811: .resolveTypeReferenceForIds(cm);
0812: String[] parentTypes = _mappingHelper
0813: .resolveTypeReferenceForIds(extendCm);
0814:
0815: if (childTypes.length != parentTypes.length) {
0816: throw new GeneratorException(
0817: "Cannot resolve type for class '"
0818: + cm.getName()
0819: + "' from extend class '"
0820: + extendCm.getName() + "'");
0821: }
0822: for (int i = 0; i < childTypes.length; i++) {
0823: if (!childTypes[i].equalsIgnoreCase(parentTypes[i])) {
0824: throw new GeneratorException(
0825: "Cannot resolve type for class '"
0826: + cm.getName()
0827: + "' from extend class '"
0828: + extendCm.getName() + "'");
0829: }
0830: }
0831: return;
0832: }
0833:
0834: boolean isUseFieldIdentity = _mappingHelper
0835: .isUseFieldIdentity(extendCm);
0836: Enumeration extendEf = extendCm.getClassChoice()
0837: .enumerateFieldMapping();
0838:
0839: // Process key generator.
0840: String keygenerator = extendCm.getKeyGenerator();
0841: KeyGenerator keyGen = null;
0842: if (keygenerator != null) {
0843: keyGen = _keyGenRegistry.getKeyGenerator(keygenerator
0844: .toUpperCase());
0845: }
0846: table.setKeyGenerator(keyGen);
0847:
0848: while (extendEf.hasMoreElements()) {
0849: FieldMapping extendFm = (FieldMapping) extendEf
0850: .nextElement();
0851:
0852: // Skip if <sql> tag is not defined.
0853: if (extendFm.getSql() == null) {
0854: continue;
0855: }
0856:
0857: boolean isFieldIdentity = extendFm.getIdentity();
0858: if (!isUseFieldIdentity) {
0859: isFieldIdentity = _mappingHelper.isIdentity(extendCm,
0860: extendFm);
0861: }
0862:
0863: // Checke for many-key, many-table definition.
0864: if (isFieldIdentity
0865: && extendFm.getSql().getManyKeyCount() <= 0) {
0866: // Column is defiend as normal column in child, but it is id which is
0867: // inherited from parent.
0868: if (mergeIfDefInBothClasses(table, cm, extendFm)) {
0869: continue;
0870: }
0871:
0872: String[] sqlnames = extendFm.getSql().getName();
0873: String sqltype = extendFm.getSql().getType();
0874:
0875: TypeInfo typeInfo = null;
0876: ClassMapping cmRef = null;
0877: String[] refIdTypes = null;
0878: boolean isUseReferenceType = false;
0879:
0880: if (sqltype != null) {
0881: typeInfo = _typeMapper.getType(sqltype);
0882: }
0883:
0884: // If typeInfo is null, this table has a reference to another one.
0885: if (typeInfo == null) {
0886: cmRef = _mappingHelper
0887: .getClassMappingByName(extendFm.getType());
0888: // If cmRef is null, the reference class could not be found.
0889: if (cmRef == null) {
0890: typeInfo = _typeMapper.getType(extendFm
0891: .getType());
0892:
0893: if (typeInfo == null) {
0894: throw new TypeNotFoundException(
0895: "can not resolve type "
0896: + extendFm.getType());
0897: }
0898: } else {
0899: isUseReferenceType = true;
0900: refIdTypes = _mappingHelper
0901: .resolveTypeReferenceForIds(extendFm
0902: .getType());
0903:
0904: // If number of reference table's Ids differ from number of
0905: // field elements.
0906: if (refIdTypes.length != sqlnames.length) {
0907: throw new TypeNotFoundException(
0908: "number of reference table's Id differs"
0909: + " to number of field elements '"
0910: + extendFm.getName()
0911: + "' of class '"
0912: + extendCm.getName() + "'"
0913: + refIdTypes.length + ","
0914: + sqlnames.length);
0915: }
0916: }
0917: }
0918:
0919: // Create fields.
0920: for (int i = 0; i < sqlnames.length; i++) {
0921: Field field = _schemaFactory.createField();
0922: field.setConfiguration(_configuration);
0923:
0924: if (isUseReferenceType) {
0925: // Each sqlname is correspond to an identity of the reference
0926: // table so, it should be possible to get the original type of
0927: // the reference field.
0928: typeInfo = _typeMapper.getType(refIdTypes[i]);
0929: if (typeInfo == null) {
0930: throw new TypeNotFoundException(
0931: "can not find reference type "
0932: + refIdTypes[i]
0933: + " of class "
0934: + extendCm.getName());
0935: }
0936: }
0937:
0938: field.setName(sqlnames[i]);
0939: field.setTable(table);
0940: field.setType(typeInfo);
0941: field.setIdentity(isFieldIdentity);
0942: field.setKeyGenerator(keyGen);
0943:
0944: if (isFieldIdentity) {
0945: table.getPrimaryKey().addField(field);
0946: }
0947:
0948: table.addField(field);
0949: }
0950: }
0951: }
0952:
0953: // Process extends.
0954: if (extendCm.getExtends() != null) {
0955: processExtendedClass(table, extendCm);
0956: }
0957: }
0958:
0959: /**
0960: * This function is used to merge a table if it is mapped to many classes.
0961: *
0962: * @param table Table to merge.
0963: * @param cm ClassMapping of table.
0964: * @param extendFm FieldMapping of extended class to be merged into table.
0965: * @return <code>true</code> if column is defiend as normal column in child, but is
0966: * identity which is inherited from parent.
0967: */
0968: private boolean mergeIfDefInBothClasses(final Table table,
0969: final ClassMapping cm, final FieldMapping extendFm) {
0970: Enumeration ef = cm.getClassChoice().enumerateFieldMapping();
0971:
0972: while (ef.hasMoreElements()) {
0973: FieldMapping fm = (FieldMapping) ef.nextElement();
0974: String fname = fm.getName();
0975: // If extend field has the same name with one of parent's fields.
0976: if (fname != null
0977: && fname.equalsIgnoreCase(extendFm.getName())) {
0978: if (fm.getSql() == null) {
0979: continue;
0980: }
0981: String[] sqlnames = fm.getSql().getName();
0982: for (int i = 0; i < sqlnames.length; i++) {
0983: table.getField(sqlnames[i]).setIdentity(true);
0984: }
0985: return true;
0986: }
0987: }
0988:
0989: return false;
0990: }
0991:
0992: /**
0993: * Add foreign key for 1:1 relations to table schema object.
0994: *
0995: * @param table Table to add foreign key to.
0996: * @param fm FieldMapping of relation.
0997: * @throws GeneratorException If failed to create foreign key schema object.
0998: */
0999: private void addOneOneForeignKey(final Table table,
1000: final FieldMapping fm) throws GeneratorException {
1001: ForeignKey fk = _schemaFactory.createForeignKey();
1002: fk.setConfiguration(_configuration);
1003:
1004: fk.setTable(table);
1005: fk.setName(table.getName() + "_" + fm.getName());
1006:
1007: String[] fieldNames = fm.getSql().getName();
1008: for (int i = 0; i < fieldNames.length; i++) {
1009: for (int j = 0; j < table.getFieldCount(); j++) {
1010: Field field = table.getField(j);
1011: if (fieldNames[i].equals(field.getName())) {
1012: fk.addField(field);
1013: }
1014: }
1015: }
1016:
1017: ClassMapping cm = _mappingHelper.getClassMappingByName(fm
1018: .getType());
1019:
1020: if (cm == null) {
1021: throw new GeneratorException("can not find class "
1022: + fm.getType());
1023: }
1024:
1025: String referenceTableName = cm.getMapTo().getTable();
1026: Table referenceTable = null;
1027: referenceTable = table.getSchema().getTable(referenceTableName);
1028: fk.setReferenceTable(referenceTable);
1029:
1030: String[] manykeys = fm.getSql().getManyKey();
1031: if (manykeys == null || manykeys.length == 0) {
1032: manykeys = _mappingHelper.getClassMappingSqlIdentity(cm,
1033: true);
1034: }
1035: for (int i = 0; i < manykeys.length; i++) {
1036: for (int j = 0; j < referenceTable.getFieldCount(); j++) {
1037: Field field = referenceTable.getField(j);
1038: if (manykeys[i].equals(field.getName())) {
1039: fk.addReferenceField(field);
1040: }
1041: }
1042: }
1043:
1044: fk.setRelationType(ForeignKey.ONE_ONE);
1045: table.addForeignKey(fk);
1046: }
1047:
1048: /**
1049: * Add column for a resolving table which is required by M:N relationship.
1050: *
1051: * @param fm FieldMapping.
1052: * @param cm ClassMapping.
1053: */
1054: private void addResolveField(final FieldMapping fm,
1055: final ClassMapping cm) {
1056: String keyGen = cm.getKeyGenerator();
1057: ClassMapping resolveCm = null;
1058:
1059: // Get table, if not existe, create one.
1060: if (_resolveTable.containsKey(fm.getSql().getManyTable())) {
1061: resolveCm = (ClassMapping) _resolveTable.get(fm.getSql()
1062: .getManyTable());
1063: } else {
1064: resolveCm = new ClassMapping();
1065: resolveCm.setName(fm.getSql().getManyTable());
1066: resolveCm.setKeyGenerator(keyGen);
1067:
1068: MapTo mapto = new MapTo();
1069: mapto.setTable(fm.getSql().getManyTable());
1070: resolveCm.setMapTo(mapto);
1071: _resolveTable.put(fm.getSql().getManyTable(), resolveCm);
1072: }
1073:
1074: FieldMapping resolveFm = new FieldMapping();
1075: resolveFm.setIdentity(true);
1076: resolveFm.setName(cm.getMapTo().getTable());
1077: resolveFm.setType(cm.getName());
1078:
1079: ClassChoice cc = resolveCm.getClassChoice();
1080: if (cc == null) {
1081: cc = new ClassChoice();
1082: resolveCm.setClassChoice(cc);
1083: }
1084: cc.addFieldMapping(resolveFm);
1085:
1086: Sql sql = new Sql();
1087: String[] sqlname = fm.getSql().getManyKey();
1088: if (sqlname == null || sqlname.length == 0) {
1089: _mappingHelper.getClassMappingSqlIdentity(cm, true);
1090: }
1091: sql.setName(sqlname);
1092: resolveFm.setSql(sql);
1093: }
1094:
1095: //--------------------------------------------------------------------------
1096: }
|