0001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/tools/datastore/DDLGenerator.java $
0002: /*---------------- FILE HEADER ------------------------------------------
0003:
0004: This file is part of deegree.
0005: Copyright (C) 2001-2008 by:
0006: EXSE, Department of Geography, University of Bonn
0007: http://www.giub.uni-bonn.de/deegree/
0008: lat/lon GmbH
0009: http://www.lat-lon.de
0010:
0011: This library is free software; you can redistribute it and/or
0012: modify it under the terms of the GNU Lesser General Public
0013: License as published by the Free Software Foundation; either
0014: version 2.1 of the License, or (at your option) any later version.
0015:
0016: This library is distributed in the hope that it will be useful,
0017: but WITHOUT ANY WARRANTY; without even the implied warranty of
0018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0019: Lesser General Public License for more details.
0020:
0021: You should have received a copy of the GNU Lesser General Public
0022: License along with this library; if not, write to the Free Software
0023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0024:
0025: Contact:
0026:
0027: Andreas Poth
0028: lat/lon GmbH
0029: Aennchenstraße 19
0030: 53177 Bonn
0031: Germany
0032: E-Mail: poth@lat-lon.de
0033:
0034: Prof. Dr. Klaus Greve
0035: Department of Geography
0036: University of Bonn
0037: Meckenheimer Allee 166
0038: 53115 Bonn
0039: Germany
0040: E-Mail: greve@giub.uni-bonn.de
0041:
0042: ---------------------------------------------------------------------------*/
0043: package org.deegree.tools.datastore;
0044:
0045: import java.io.File;
0046: import java.io.FileWriter;
0047: import java.io.IOException;
0048: import java.io.PrintWriter;
0049: import java.net.MalformedURLException;
0050: import java.net.URL;
0051: import java.util.ArrayList;
0052: import java.util.Collection;
0053: import java.util.HashMap;
0054: import java.util.HashSet;
0055: import java.util.Iterator;
0056: import java.util.LinkedHashMap;
0057: import java.util.Map;
0058: import java.util.Set;
0059:
0060: import org.deegree.datatypes.Types;
0061: import org.deegree.datatypes.UnknownTypeException;
0062: import org.deegree.framework.xml.XMLParsingException;
0063: import org.deegree.framework.xml.schema.XMLSchemaException;
0064: import org.deegree.io.datastore.schema.MappedFeaturePropertyType;
0065: import org.deegree.io.datastore.schema.MappedFeatureType;
0066: import org.deegree.io.datastore.schema.MappedGMLId;
0067: import org.deegree.io.datastore.schema.MappedGMLSchema;
0068: import org.deegree.io.datastore.schema.MappedGMLSchemaDocument;
0069: import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
0070: import org.deegree.io.datastore.schema.MappedPropertyType;
0071: import org.deegree.io.datastore.schema.MappedSimplePropertyType;
0072: import org.deegree.io.datastore.schema.TableRelation;
0073: import org.deegree.io.datastore.schema.content.MappingField;
0074: import org.deegree.io.datastore.schema.content.MappingGeometryField;
0075: import org.deegree.io.datastore.schema.content.SimpleContent;
0076: import org.deegree.io.datastore.sql.idgenerator.DBSeqIdGenerator;
0077: import org.deegree.model.crs.UnknownCRSException;
0078: import org.deegree.model.feature.schema.FeatureType;
0079: import org.deegree.model.feature.schema.PropertyType;
0080: import org.xml.sax.SAXException;
0081:
0082: /**
0083: * Abstract base class for DDL generation from annotated GML schema files.
0084: * <p>
0085: * This abstract base class only implements the functionality needed to retrieve the necessary
0086: * tables and columns used in an annotated GML schema. Some DDL generation may be dependent on the
0087: * specific SQL backend to be used, so this is implemented in concrete extensions of this class.
0088: *
0089: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a>
0090: * @author last edited by: $Author: apoth $
0091: *
0092: * @version $Revision: 9346 $, $Date: 2007-12-27 08:39:07 -0800 (Thu, 27 Dec 2007) $
0093: */
0094: public abstract class DDLGenerator {
0095:
0096: protected static final String FT_PREFIX = "FT_";
0097:
0098: protected static final int FEATURE_TYPE_TABLE = 0;
0099:
0100: protected static final int JOIN_TABLE = 1;
0101:
0102: protected static final int MULTI_PROPERTY_TABLE = 2;
0103:
0104: protected MappedGMLSchema schema;
0105:
0106: // key type: String (table names), value type: TableDefinition
0107: protected Map<String, TableDefinition> tables = new HashMap<String, TableDefinition>();
0108:
0109: // names of sequences (for id generation)
0110: protected Set<String> sequences = new HashSet<String>();
0111:
0112: /**
0113: * Generates the DDL statements necessary for the creation of the given table definition. Must
0114: * be overwritten by the concrete implementation.
0115: *
0116: * @param table
0117: * @return the DDL statements necessary for the creation of the given table definition
0118: */
0119: protected abstract StringBuffer generateCreateTableStmt(
0120: TableDefinition table);
0121:
0122: /**
0123: * Generates the DDL statements necessary for the creation of standard indexes for the given
0124: * table definition. Must be overwritten by the concrete implementation.
0125: *
0126: * @param table
0127: * @return the DDL statements necessary for the creation of standard indexes for the given table
0128: * definition
0129: */
0130: protected abstract StringBuffer generateCreateIndexStmts(
0131: TableDefinition table);
0132:
0133: /**
0134: * Generates the DDL statements necessary for the creation of the given sequence. May be
0135: * overwritten by a concrete implementation.
0136: *
0137: * @param sequenceName
0138: * @return the DDL statements necessary for the creation of the given sequence definition
0139: */
0140: protected StringBuffer generateCreateSequenceStmt(
0141: String sequenceName) {
0142: StringBuffer sb = new StringBuffer("CREATE SEQUENCE ");
0143: sb.append(sequenceName);
0144: sb.append(";\n");
0145: return sb;
0146: }
0147:
0148: /**
0149: * Generates the DDL statements necessary for the removal of the given table definition. May be
0150: * overwritten by a concrete implementation.
0151: *
0152: * @param table
0153: * @return the DDL statements necessary for the removal of the given table definition
0154: */
0155: protected StringBuffer generateDropTableStmt(TableDefinition table) {
0156: StringBuffer sb = new StringBuffer();
0157: sb.append("DROP TABLE ");
0158: sb.append(table.getName());
0159: sb.append(" CASCADE;\n");
0160: return sb;
0161: }
0162:
0163: /**
0164: * Generates the DDL statements necessary for the dropping of standard indexes for the given
0165: * table definition. May be overwritten by a concrete implementation.
0166: *
0167: * @param table
0168: * @return the DDL statements necessary for the dropping of standard indexes for the given table
0169: * definition
0170: */
0171: protected StringBuffer generateDropIndexStmts(TableDefinition table) {
0172: StringBuffer sb = new StringBuffer();
0173:
0174: // build drop statements for geometry indexes
0175: Collection<ColumnDefinition> geometryColumns = new ArrayList<ColumnDefinition>();
0176: for (ColumnDefinition column : table.getColumns()) {
0177: if (column.isGeometry()) {
0178: geometryColumns.add(column);
0179: }
0180: }
0181:
0182: Iterator<ColumnDefinition> iter = geometryColumns.iterator();
0183: int spatialIdxCnt = 1;
0184: while (iter.hasNext()) {
0185: iter.next();
0186: sb.append("DROP INDEX ");
0187: sb.append(table.getName() + (spatialIdxCnt++));
0188: sb.append("_SPATIAL_IDX;");
0189: sb.append('\n');
0190: }
0191:
0192: // build table type specific drop index statements
0193: switch (table.getType()) {
0194: case JOIN_TABLE: {
0195: // create an index on every column
0196: ColumnDefinition[] columns = table.getColumns();
0197: for (int i = 0; i < columns.length; i++) {
0198: sb.append("DROP INDEX ");
0199: sb.append(table.getName().toUpperCase());
0200: sb.append('_');
0201: sb.append(columns[i].getName());
0202: sb.append(';');
0203: sb.append('\n');
0204: }
0205: break;
0206: }
0207: default: {
0208: break;
0209: }
0210: }
0211: return sb;
0212: }
0213:
0214: /**
0215: * Generates the DDL statements necessary for the removal of the given sequence. May be
0216: * overwritten by a concrete implementation.
0217: *
0218: * @param sequenceName
0219: * @return the DDL statements necessary for the removal of the given sequence definition
0220: */
0221: protected StringBuffer generateDropSequenceStmt(String sequenceName) {
0222: StringBuffer sb = new StringBuffer("DROP SEQUENCE ");
0223: sb.append(sequenceName);
0224: sb.append(";\n");
0225: return sb;
0226: }
0227:
0228: /**
0229: * Creates a new instance of <code>DDLGenerator</code> from the given parameters.
0230: *
0231: * @param schemaURL
0232: * @throws MalformedURLException
0233: * @throws IOException
0234: * @throws SAXException
0235: * @throws XMLParsingException
0236: * @throws XMLSchemaException
0237: * @throws UnknownCRSException
0238: */
0239: protected DDLGenerator(URL schemaURL) throws MalformedURLException,
0240: IOException, SAXException, XMLParsingException,
0241: XMLSchemaException, UnknownCRSException {
0242:
0243: System.out.println(Messages.format("LOADING_SCHEMA_FILE",
0244: schemaURL));
0245: MappedGMLSchemaDocument schemaDoc = new MappedGMLSchemaDocument();
0246: schemaDoc.load(schemaURL);
0247: schema = schemaDoc.parseMappedGMLSchema();
0248: FeatureType[] featureTypes = schema.getFeatureTypes();
0249: int concreteCount = 0;
0250: for (int i = 0; i < featureTypes.length; i++) {
0251: if (!featureTypes[i].isAbstract()) {
0252: concreteCount++;
0253: }
0254: }
0255: System.out.println(Messages.format("SCHEMA_INFO", new Integer(
0256: featureTypes.length), new Integer(featureTypes.length
0257: - concreteCount), new Integer(concreteCount)));
0258: System.out.println(Messages.getString("RETRIEVING_TABLES"));
0259: buildTableMap();
0260: }
0261:
0262: /**
0263: * Returns all table definitions of the given type.
0264: *
0265: * @param type
0266: * FEATURE_TYPE_TABLE, JOIN_TABLE or MULTI_PROPERTY_TABLE
0267: * @return all table definitions of the given type.
0268: */
0269: protected TableDefinition[] getTables(int type) {
0270: Collection<TableDefinition> tableList = new ArrayList<TableDefinition>();
0271: Iterator<String> iter = this .tables.keySet().iterator();
0272: while (iter.hasNext()) {
0273: String tableName = iter.next();
0274: TableDefinition table = this .tables.get(tableName);
0275: if (table.getType() == type) {
0276: tableList.add(table);
0277: }
0278: }
0279: return tableList.toArray(new TableDefinition[tableList.size()]);
0280: }
0281:
0282: /**
0283: * Returns the table definition for the table with the given name. If no such definition exists,
0284: * a new table definition is created and added to the internal <code>tables</code> map.
0285: *
0286: * @param tableName
0287: * table definition to look up
0288: * @param type
0289: * type of the table (only respected, if a new TableDefinition instance is created)
0290: * @return the table definition for the table with the given name.
0291: */
0292: private TableDefinition lookupTableDefinition(String tableName,
0293: int type) {
0294: TableDefinition table = this .tables.get(tableName);
0295: if (table == null) {
0296: table = new TableDefinition(tableName, type);
0297: this .tables.put(tableName, table);
0298: }
0299: return table;
0300: }
0301:
0302: /**
0303: * Collects the referenced tables and their columns from the input schema. Builds the member map
0304: * <code>tables</code> from this data.
0305: */
0306: private void buildTableMap() {
0307: FeatureType[] featureTypes = schema.getFeatureTypes();
0308: for (int i = 0; i < featureTypes.length; i++) {
0309: if (!featureTypes[i].isAbstract()) {
0310: buildTableMap((MappedFeatureType) featureTypes[i]);
0311: }
0312: }
0313: }
0314:
0315: /**
0316: * Collects the tables and their columns used in the annotation of the given feature type.
0317: * Builds the member map <code>tables</code> from this data.
0318: *
0319: * @param featureType
0320: * feature type to process
0321: */
0322: private void buildTableMap(MappedFeatureType featureType) {
0323: TableDefinition table = lookupTableDefinition(featureType
0324: .getTable(), FEATURE_TYPE_TABLE);
0325:
0326: MappedGMLId gmlId = featureType.getGMLId();
0327: addGMLIdColumns(gmlId, table);
0328:
0329: if (gmlId.getIdGenerator() instanceof DBSeqIdGenerator) {
0330: extractSequence((DBSeqIdGenerator) featureType.getGMLId()
0331: .getIdGenerator());
0332: }
0333:
0334: PropertyType[] properties = featureType.getProperties();
0335: for (int i = 0; i < properties.length; i++) {
0336: MappedPropertyType property = (MappedPropertyType) properties[i];
0337: if (property instanceof MappedSimplePropertyType) {
0338: buildTableMap((MappedSimplePropertyType) property,
0339: table);
0340: } else if (property instanceof MappedGeometryPropertyType) {
0341: buildTableMap((MappedGeometryPropertyType) property,
0342: table);
0343: } else if (property instanceof MappedFeaturePropertyType) {
0344: buildTableMap((MappedFeaturePropertyType) property,
0345: table);
0346: } else {
0347: throw new RuntimeException(Messages.format(
0348: "ERROR_UNEXPECTED_PROPERTY_TYPE", property
0349: .getClass().getName()));
0350: }
0351: }
0352: }
0353:
0354: /**
0355: * Adds the name of the sequence that the given {@link DBSeqIdGenerator} refers to.
0356: *
0357: * @param idGenerator
0358: * generator instance
0359: */
0360: private void extractSequence(DBSeqIdGenerator idGenerator) {
0361: this .sequences.add(idGenerator.getSequenceName());
0362: }
0363:
0364: /**
0365: * Adds the columns used in the given <code>MappedGMLId</code> to the also given
0366: * <code>TableDefinition</code>.
0367: *
0368: * @param gmlId
0369: * columns are taken from this gmlId mapping
0370: * @param table
0371: * columns are added to this table definition
0372: */
0373: private void addGMLIdColumns(MappedGMLId gmlId,
0374: TableDefinition table) {
0375: MappingField[] idFields = gmlId.getIdFields();
0376: for (int i = 0; i < idFields.length; i++) {
0377: ColumnDefinition column = new ColumnDefinition(idFields[i]
0378: .getField(), idFields[i].getType(), false, true,
0379: false, -1);
0380: table.addColumn(column);
0381: }
0382: }
0383:
0384: /**
0385: * Collects the tables and their columns used in the annotation of the given simple property
0386: * type. Builds the <code>table</code> member map from this data.
0387: * <p>
0388: * If the data for the property is stored in a related table, the table and column information
0389: * used on the path to this table is also added to the <code>tables</code> member map.
0390: *
0391: * @param simpleProperty
0392: * simple property type to process
0393: * @param table
0394: * table definition associated with the property definition
0395: */
0396: private void buildTableMap(MappedSimplePropertyType simpleProperty,
0397: TableDefinition table) {
0398: Collection<ColumnDefinition> newColumns = new ArrayList<ColumnDefinition>();
0399: // array must always have length 1
0400: TableRelation[] relations = simpleProperty.getTableRelations();
0401: if (simpleProperty.getMaxOccurs() != 1
0402: && (relations == null || relations.length < 1)) {
0403: throw new RuntimeException(Messages.format(
0404: "ERROR_INVALID_PROPERTY_DEFINITION", simpleProperty
0405: .getName()));
0406: }
0407:
0408: SimpleContent content = simpleProperty.getContent();
0409: if (content instanceof MappingField) {
0410: MappingField mf = (MappingField) content;
0411: if (relations == null || relations.length == 0) {
0412: newColumns.add(new ColumnDefinition(mf.getField(), mf
0413: .getType(), simpleProperty.getMinOccurs() == 0,
0414: false, -1));
0415: } else {
0416: TableRelation firstRelation = relations[0];
0417: MappingField[] fromFields = firstRelation
0418: .getFromFields();
0419: for (int i = 0; i < fromFields.length; i++) {
0420: MappingField fromField = fromFields[i];
0421: newColumns.add(new ColumnDefinition(fromField
0422: .getField(), fromField.getType(), false,
0423: false, -1));
0424: }
0425: buildTableMap(relations, mf);
0426: }
0427: } else {
0428: String msg = "Ignoring property '" + simpleProperty
0429: + "' - has virtual content.";
0430: System.out.println(msg);
0431: }
0432: table.addColumns(newColumns);
0433: }
0434:
0435: /**
0436: * Collects the tables and their columns used in the annotation of the given geometry property
0437: * type. Builds the <code>table</code> member map from this data.
0438: * <p>
0439: * If the geometry for the property is stored in a related table, the table and column
0440: * information used on the path to this table is also added to the <code>tables</code> member
0441: * map.
0442: *
0443: * @param geometryProperty
0444: * feature property type to process
0445: * @param table
0446: * table definition associated with the property definition
0447: */
0448: private void buildTableMap(
0449: MappedGeometryPropertyType geometryProperty,
0450: TableDefinition table) {
0451: Collection<ColumnDefinition> newColumns = new ArrayList<ColumnDefinition>();
0452: TableRelation[] relations = geometryProperty
0453: .getTableRelations();
0454: if (geometryProperty.getMaxOccurs() != 1
0455: && (relations == null || relations.length < 1)) {
0456: throw new RuntimeException(Messages.format(
0457: "ERROR_INVALID_PROPERTY_DEFINITION",
0458: geometryProperty.getName()));
0459: }
0460: if (relations == null || relations.length == 0) {
0461: newColumns.add(new ColumnDefinition(geometryProperty
0462: .getMappingField().getField(), geometryProperty
0463: .getMappingField().getType(), geometryProperty
0464: .getMinOccurs() == 0, true, geometryProperty
0465: .getMappingField().getSRS()));
0466: } else {
0467: TableRelation firstRelation = relations[0];
0468: MappingField[] fromFields = firstRelation.getFromFields();
0469: for (int i = 0; i < fromFields.length; i++) {
0470: MappingField fromField = fromFields[i];
0471: newColumns.add(new ColumnDefinition(fromField
0472: .getField(), fromField.getType(), false, true,
0473: geometryProperty.getMappingField().getSRS()));
0474: }
0475: buildTableMap(relations, geometryProperty.getMappingField());
0476: }
0477: table.addColumns(newColumns);
0478: }
0479:
0480: /**
0481: * Collects the tables and their columns used in the annotation of the given feature property
0482: * type. Builds the <code>table</code> member map from this data.
0483: * <p>
0484: * The table and column information used on the path to the table of the feature type is also
0485: * added to the <code>tables</code> member map.
0486: *
0487: * @param featureProperty
0488: * feature property type to process
0489: * @param table
0490: * table definition associated with the property definition
0491: */
0492: private void buildTableMap(
0493: MappedFeaturePropertyType featureProperty,
0494: TableDefinition table) {
0495: Collection<ColumnDefinition> newColumns = new ArrayList<ColumnDefinition>();
0496:
0497: // array must always have length 1
0498: TableRelation[] relations = featureProperty.getTableRelations();
0499:
0500: // target feature type table must always be accessed via 'Relation'-elements
0501: if (relations == null || relations.length < 1) {
0502: throw new RuntimeException(Messages.format(
0503: "ERROR_INVALID_FEATURE_PROPERTY_DEFINITION_1",
0504: featureProperty.getName()));
0505: }
0506:
0507: // maxOccurs > 1: target feature type table must be accessed via join table
0508: if (featureProperty.getMaxOccurs() != 1
0509: && (relations.length < 2)) {
0510: throw new RuntimeException(Messages.format(
0511: "ERROR_INVALID_FEATURE_PROPERTY_DEFINITION_2",
0512: featureProperty.getName()));
0513: }
0514:
0515: // add this feature type's key columns to current table
0516: TableRelation firstRelation = relations[0];
0517: MappingField[] fromFields = firstRelation.getFromFields();
0518: boolean isNullable = featureProperty.getMinOccurs() == 0
0519: && relations.length == 1;
0520: for (int i = 0; i < fromFields.length; i++) {
0521: MappingField fromField = fromFields[i];
0522: newColumns.add(new ColumnDefinition(fromField.getField(),
0523: fromField.getType(), isNullable, false, -1));
0524: }
0525: table.addColumns(newColumns);
0526:
0527: MappedFeatureType contentType = featureProperty
0528: .getFeatureTypeReference().getFeatureType();
0529: buildTableMap(relations, featureProperty, contentType);
0530: }
0531:
0532: /**
0533: * Collects the tables and their columns used in the relation tables from a simple/geometry
0534: * property to it's content table. Builds the <code>table</code> member map from this data.
0535: *
0536: * @param relations
0537: * relation tables from annotation of property type
0538: * @param targetField
0539: * holds the properties data
0540: */
0541: private void buildTableMap(TableRelation[] relations,
0542: MappingField targetField) {
0543:
0544: // process tables used in 'To'-element of each 'Relation'-element
0545: for (int i = 0; i < relations.length; i++) {
0546: String tableName = relations[i].getToTable();
0547: TableDefinition table = lookupTableDefinition(tableName,
0548: MULTI_PROPERTY_TABLE);
0549: MappingField[] toFields = relations[i].getToFields();
0550: for (int j = 0; j < toFields.length; j++) {
0551: boolean toIsFK = relations[i].getFKInfo() == TableRelation.FK_INFO.fkIsToField;
0552: ColumnDefinition column = new ColumnDefinition(
0553: toFields[j].getField(), toFields[j].getType(),
0554: false, !toIsFK, false, -1);
0555: // schmitz: assuming not part of PK
0556: table.addColumn(column);
0557: }
0558: }
0559:
0560: // process table used in 'To'-element of last 'Relation'-element (targetField refers to
0561: // this)
0562: ColumnDefinition column = null;
0563: if (targetField instanceof MappingGeometryField) {
0564:
0565: column = new ColumnDefinition(targetField.getField(),
0566: targetField.getType(), false, true,
0567: ((MappingGeometryField) targetField).getSRS());
0568: } else {
0569: column = new ColumnDefinition(targetField.getField(),
0570: targetField.getType(), false, false, -1);
0571: }
0572:
0573: TableDefinition table = lookupTableDefinition(
0574: relations[relations.length - 1].getToTable(),
0575: MULTI_PROPERTY_TABLE);
0576: table.addColumn(column);
0577: }
0578:
0579: /**
0580: * Collects the tables and their columns used in the relation tables from a feature property to
0581: * it's content feature type. Builds the <code>table</code> member map from this data.
0582: *
0583: * @param relations
0584: * relation tables from annotation of feature property type
0585: * @param property
0586: * @param targetType
0587: * type contained in the feature property
0588: */
0589: private void buildTableMap(TableRelation[] relations,
0590: MappedPropertyType property, MappedFeatureType targetType) {
0591:
0592: TableDefinition table = lookupTableDefinition(relations[0]
0593: .getFromTable(), FEATURE_TYPE_TABLE);
0594:
0595: // process tables used in 'To'-element of each 'Relation'-element (except the last)
0596: for (int i = 0; i < relations.length - 1; i++) {
0597: String tableName = relations[i].getToTable();
0598: table = lookupTableDefinition(tableName, JOIN_TABLE);
0599: MappingField[] toFields = relations[i].getToFields();
0600: for (int j = 0; j < toFields.length; j++) {
0601: // boolean toIsFK = relations[i].getFKInfo() == TableRelation.FK_INFO.fkIsToField;
0602: ColumnDefinition column = new ColumnDefinition(
0603: toFields[j].getField(), toFields[j].getType(),
0604: false, true, false, -1);
0605: // schmitz: assuming NOT part of PK
0606: table.addColumn(column);
0607: }
0608: }
0609:
0610: // process table used in 'To'-element of last 'Relation'-element
0611: MappedFeatureType[] concreteTypes = targetType
0612: .getConcreteSubstitutions();
0613: MappingField[] toFields = relations[relations.length - 1]
0614: .getToFields();
0615:
0616: // if it refers to several target tables (target feature type is abstract), an additional
0617: // column is needed (which determines the target feature type)
0618: if (concreteTypes.length > 1) {
0619: String typeColumn = "featuretype";
0620: if (relations.length == 1) {
0621: typeColumn = FT_PREFIX
0622: + property.getTableRelations()[0]
0623: .getFromFields()[0].getField();
0624: }
0625: ColumnDefinition column = new ColumnDefinition(typeColumn,
0626: Types.VARCHAR, property.getMinOccurs() == 0, false,
0627: -1);
0628: table.addColumn(column);
0629: }
0630: for (int i = 0; i < concreteTypes.length; i++) {
0631: MappedFeatureType concreteType = concreteTypes[i];
0632: String tableName = concreteType.getTable();
0633: table = lookupTableDefinition(tableName, FEATURE_TYPE_TABLE);
0634: for (int j = 0; j < toFields.length; j++) {
0635: ColumnDefinition column = new ColumnDefinition(
0636: toFields[j].getField(), toFields[j].getType(),
0637: false, false, -1);
0638: table.addColumn(column);
0639: }
0640: }
0641:
0642: // process tables used in 'From'-element of each 'Relation'-element (except the first)
0643: for (int i = 1; i < relations.length; i++) {
0644: String tableName = relations[i].getFromTable();
0645: if (i != relations.length - 1) {
0646: table = lookupTableDefinition(tableName, JOIN_TABLE);
0647: } else {
0648: table = lookupTableDefinition(tableName,
0649: FEATURE_TYPE_TABLE);
0650: }
0651: MappingField[] fromFields = relations[i].getFromFields();
0652: for (int j = 0; j < fromFields.length; j++) {
0653: ColumnDefinition column = new ColumnDefinition(
0654: fromFields[j].getField(), fromFields[j]
0655: .getType(), false, true, false, -1);
0656: table.addColumn(column);
0657: }
0658: }
0659: }
0660:
0661: /**
0662: * Generates the DDL statements that can be used to build a relational schema that backs the GML
0663: * schema.
0664: *
0665: * @param outputFile
0666: * @throws IOException
0667: */
0668: public void generateCreateScript(String outputFile)
0669: throws IOException {
0670: PrintWriter writer = new PrintWriter(new FileWriter(outputFile));
0671:
0672: System.out.println(Messages.format("CREATE_SEQUENCES",
0673: new Integer(sequences.size())));
0674: if (sequences.size() > 0) {
0675: writer.print("/* CREATE SEQUENCES (" + sequences.size()
0676: + ") */\n");
0677: for (String sequenceName : sequences) {
0678: writer.print('\n');
0679: writer.print(generateCreateSequenceStmt(sequenceName));
0680: }
0681: }
0682:
0683: TableDefinition[] tables = getTables(FEATURE_TYPE_TABLE);
0684: System.out.println(Messages.format("CREATE_FEATURE_TYPE",
0685: new Integer(tables.length)));
0686: writer.print("\n\n/* CREATE FEATURE TABLES (" + tables.length
0687: + ") */\n");
0688: for (int i = 0; i < tables.length; i++) {
0689: System.out.println(tables[i].tableName);
0690: writer.print('\n');
0691: writer.print(generateCreateTableStmt(tables[i]));
0692: writer.print(generateCreateIndexStmts(tables[i]));
0693: }
0694:
0695: tables = getTables(JOIN_TABLE);
0696: if (tables.length != 0) {
0697: writer.print("\n\n/* CREATE JOIN TABLES (" + tables.length
0698: + ") */\n");
0699: }
0700: System.out.println(Messages.format("CREATE_JOIN_TABLES",
0701: new Integer(tables.length)));
0702: for (int i = 0; i < tables.length; i++) {
0703: System.out.println(tables[i].tableName);
0704: writer.print('\n');
0705: writer.print(generateCreateTableStmt(tables[i]));
0706: writer.print(generateCreateIndexStmts(tables[i]));
0707: }
0708:
0709: tables = getTables(MULTI_PROPERTY_TABLE);
0710: if (tables.length != 0) {
0711: writer.print("\n\n/* CREATE PROPERTY TABLES ("
0712: + tables.length + ") */\n");
0713: }
0714: System.out.println(Messages.format("CREATE_PROPERTY_TABLES",
0715: new Integer(tables.length)));
0716: for (int i = 0; i < tables.length; i++) {
0717: System.out.println(tables[i].tableName);
0718: writer.print('\n');
0719: writer.print(generateCreateTableStmt(tables[i]));
0720: writer.print(generateCreateIndexStmts(tables[i]));
0721: }
0722: writer.close();
0723: }
0724:
0725: /**
0726: * Generates the DDL statements that can be used to remove the relational schema again.
0727: *
0728: * @param outputFile
0729: * @throws IOException
0730: */
0731: public void generateDropScript(String outputFile)
0732: throws IOException {
0733: PrintWriter writer = new PrintWriter(new FileWriter(outputFile));
0734:
0735: TableDefinition[] tables = getTables(FEATURE_TYPE_TABLE);
0736: System.out.println(Messages.format("DROP_FEATURE_TYPE",
0737: new Integer(tables.length)));
0738: writer.print("/* DROP FEATURE TABLES (" + tables.length
0739: + ") */\n");
0740: for (int i = 0; i < tables.length; i++) {
0741: writer.print('\n');
0742: writer.print(generateDropIndexStmts(tables[i]));
0743: writer.print(generateDropTableStmt(tables[i]));
0744: }
0745:
0746: tables = getTables(JOIN_TABLE);
0747: writer.print("\n\n/* DROP JOIN TABLES (" + tables.length
0748: + ") */\n");
0749: System.out.println(Messages.format("DROP_JOIN_TABLES",
0750: new Integer(tables.length)));
0751: for (int i = 0; i < tables.length; i++) {
0752: writer.print('\n');
0753: writer.print(generateDropIndexStmts(tables[i]));
0754: writer.print(generateDropTableStmt(tables[i]));
0755: }
0756:
0757: tables = getTables(MULTI_PROPERTY_TABLE);
0758: writer.print("\n\n/* DROP PROPERTY TABLES (" + tables.length
0759: + ") */\n");
0760: System.out.println(Messages.format("DROP_PROPERTY_TABLES",
0761: new Integer(tables.length)));
0762: for (int i = 0; i < tables.length; i++) {
0763: writer.print('\n');
0764: writer.print(generateDropIndexStmts(tables[i]));
0765: writer.print(generateDropTableStmt(tables[i]));
0766: }
0767:
0768: System.out.println(Messages.format("DROP_SEQUENCES",
0769: new Integer(sequences.size())));
0770: if (sequences.size() > 0) {
0771: writer.print("\n\n/* DROP SEQUENCES (" + sequences.size()
0772: + ") */\n");
0773: for (String sequenceName : sequences) {
0774: writer.print('\n');
0775: writer.print(generateDropSequenceStmt(sequenceName));
0776: }
0777: }
0778:
0779: writer.close();
0780: }
0781:
0782: /**
0783: * @param args
0784: * @throws IOException
0785: * @throws SAXException
0786: * @throws XMLParsingException
0787: * @throws XMLSchemaException
0788: * @throws UnknownCRSException
0789: */
0790: public static void main(String[] args) throws IOException,
0791: SAXException, XMLParsingException, XMLSchemaException,
0792: UnknownCRSException {
0793:
0794: if (args.length != 4) {
0795: System.out
0796: .println("Usage: DDLGenerator [FLAVOUR] <input.xsd> <create.sql> <drop.sql>");
0797: System.exit(0);
0798: }
0799:
0800: String flavour = args[0];
0801: String schemaFile = args[1];
0802: String createFile = args[2];
0803: String dropFile = args[3];
0804:
0805: DDLGenerator generator = null;
0806: if ("POSTGIS".equals(flavour)) {
0807: generator = new PostGISDDLGenerator(new File(schemaFile)
0808: .toURI().toURL());
0809: } else if ("ORACLE".equals(flavour)) {
0810: generator = new OracleDDLGenerator(new File(schemaFile)
0811: .toURI().toURL());
0812: } else {
0813: System.out.println(Messages.format(
0814: "ERROR_UNSUPPORTED_FLAVOUR", flavour));
0815: }
0816:
0817: generator.generateCreateScript(createFile);
0818: generator.generateDropScript(dropFile);
0819: }
0820:
0821: /**
0822: * Returns a string representation of the object.
0823: *
0824: * @return a string representation of the object.
0825: */
0826: @Override
0827: public String toString() {
0828: StringBuffer sb = new StringBuffer(Messages
0829: .getString("RELATIONAL_SCHEMA"));
0830: sb.append('\n');
0831:
0832: TableDefinition[] tables = getTables(FEATURE_TYPE_TABLE);
0833: sb.append('\n');
0834: sb.append(tables.length);
0835: sb.append(" feature type tables\n\n");
0836: for (int i = 0; i < tables.length; i++) {
0837: sb.append(tables[i]);
0838: sb.append('\n');
0839: }
0840:
0841: sb.append('\n');
0842: tables = getTables(JOIN_TABLE);
0843: sb.append(tables.length);
0844: sb.append(" join tables\n\n");
0845: for (int i = 0; i < tables.length; i++) {
0846: sb.append(tables[i]);
0847: sb.append('\n');
0848: }
0849:
0850: sb.append('\n');
0851: tables = getTables(MULTI_PROPERTY_TABLE);
0852: sb.append(tables.length);
0853: sb.append(" property tables\n\n");
0854: for (int i = 0; i < tables.length; i++) {
0855: sb.append(tables[i]);
0856: sb.append('\n');
0857: }
0858: return sb.toString();
0859: }
0860:
0861: class TableDefinition {
0862:
0863: private int type;
0864:
0865: String tableName;
0866:
0867: private Map<String, ColumnDefinition> columnsMap = new LinkedHashMap<String, ColumnDefinition>();
0868:
0869: TableDefinition(String tableName, int type) {
0870: this .type = type;
0871: this .tableName = tableName;
0872: }
0873:
0874: String getName() {
0875: return this .tableName;
0876: }
0877:
0878: int getType() {
0879: return this .type;
0880: }
0881:
0882: ColumnDefinition[] getColumns() {
0883: Collection<ColumnDefinition> columns = new ArrayList<ColumnDefinition>();
0884: Iterator<String> iter = columnsMap.keySet().iterator();
0885: while (iter.hasNext()) {
0886: String columnName = iter.next();
0887: columns.add(columnsMap.get(columnName));
0888: }
0889: return columns
0890: .toArray(new ColumnDefinition[columns.size()]);
0891: }
0892:
0893: ColumnDefinition[] getPKColumns() {
0894: Collection<ColumnDefinition> columns = new ArrayList<ColumnDefinition>();
0895: Iterator<String> iter = columnsMap.keySet().iterator();
0896: while (iter.hasNext()) {
0897: String columnName = iter.next();
0898: ColumnDefinition column = columnsMap.get(columnName);
0899: if (column.isPartOfPK()) {
0900: columns.add(columnsMap.get(columnName));
0901: }
0902: }
0903: return columns
0904: .toArray(new ColumnDefinition[columns.size()]);
0905: }
0906:
0907: ColumnDefinition getColumn(String name) {
0908: return columnsMap.get(name);
0909: }
0910:
0911: void addColumn(ColumnDefinition column) {
0912: ColumnDefinition oldColumn = columnsMap.get(column
0913: .getName());
0914: if (oldColumn != null) {
0915: if (!(column.getType() == oldColumn.getType())) {
0916: String msg = Messages.format(
0917: "ERROR_COLUMN_DEFINITION_TYPES", column
0918: .getName(),
0919: oldColumn.isNullable() ? "NULLABLE"
0920: : "NOT NULLABLE", column
0921: .isNullable() ? "NULLABLE"
0922: : "NOT NULLABLE");
0923: throw new RuntimeException(msg);
0924:
0925: }
0926: if (oldColumn.isPartOfPK()) {
0927: column = oldColumn;
0928: }
0929: }
0930: columnsMap.put(column.getName(), column);
0931: }
0932:
0933: void addColumns(Collection<ColumnDefinition> columns) {
0934: Iterator<ColumnDefinition> iter = columns.iterator();
0935: while (iter.hasNext()) {
0936: ColumnDefinition column = iter.next();
0937: addColumn(column);
0938: }
0939: }
0940:
0941: @Override
0942: public String toString() {
0943: StringBuffer sb = new StringBuffer();
0944: sb.append(Messages.format("TABLE", this .tableName));
0945: sb.append(Messages.getString("PRIMARY_KEY"));
0946: ColumnDefinition[] pkColumns = getPKColumns();
0947: for (int i = 0; i < pkColumns.length; i++) {
0948: sb.append('"');
0949: sb.append(pkColumns[i].getName());
0950: sb.append('"');
0951: if (i != pkColumns.length - 1) {
0952: sb.append(", ");
0953: }
0954: }
0955: sb.append('\n');
0956: Iterator<String> columnNameIter = this .columnsMap.keySet()
0957: .iterator();
0958: while (columnNameIter.hasNext()) {
0959: String columnName = columnNameIter.next();
0960: ColumnDefinition column = this .columnsMap
0961: .get(columnName);
0962: try {
0963: sb.append(Messages.format("COLUMN", columnName,
0964: Types.getTypeNameForSQLTypeCode(column
0965: .getType())
0966: + ":" + column.getType(),
0967: new Boolean(column.isNullable())));
0968: } catch (UnknownTypeException e) {
0969: // TODO Auto-generated catch block
0970: e.printStackTrace();
0971: }
0972: sb.append('\n');
0973: }
0974: return sb.toString();
0975: }
0976: }
0977:
0978: class ColumnDefinition {
0979:
0980: private String columnName;
0981:
0982: private int type;
0983:
0984: private boolean isNullable;
0985:
0986: private boolean isGeometryColumn;
0987:
0988: private int srsCode;
0989:
0990: private boolean isPartOfPK;
0991:
0992: ColumnDefinition(String columnName, int type,
0993: boolean isNullable, boolean isGeometryColumn,
0994: int srsCode) {
0995: this .columnName = columnName;
0996: this .type = type;
0997: this .isNullable = isNullable;
0998: this .isGeometryColumn = isGeometryColumn;
0999: this .srsCode = srsCode;
1000: }
1001:
1002: ColumnDefinition(String columnName, int type,
1003: boolean isNullable, boolean isPartOfPK,
1004: boolean isGeometryColumn, int srsCode) {
1005: this (columnName, type, isNullable, isGeometryColumn,
1006: srsCode);
1007: this .isPartOfPK = isPartOfPK;
1008: }
1009:
1010: String getName() {
1011: return this .columnName;
1012: }
1013:
1014: int getType() {
1015: return this .type;
1016: }
1017:
1018: boolean isNullable() {
1019: return this .isNullable;
1020: }
1021:
1022: boolean isGeometry() {
1023: return this .isGeometryColumn;
1024: }
1025:
1026: int getSRS() {
1027: return this .srsCode;
1028: }
1029:
1030: boolean isPartOfPK() {
1031: return this.isPartOfPK;
1032: }
1033: }
1034: }
|