001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/tools/datastore/DDLGenerator.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2007 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstraße 19 030 53177 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.tools.datastore; 044 045 import java.io.File; 046 import java.io.FileWriter; 047 import java.io.IOException; 048 import java.io.PrintWriter; 049 import java.net.MalformedURLException; 050 import java.net.URL; 051 import java.util.ArrayList; 052 import java.util.Collection; 053 import java.util.HashMap; 054 import java.util.Iterator; 055 import java.util.LinkedHashMap; 056 import java.util.Map; 057 058 import org.deegree.datatypes.Types; 059 import org.deegree.datatypes.UnknownTypeException; 060 import org.deegree.framework.xml.XMLParsingException; 061 import org.deegree.framework.xml.schema.XMLSchemaException; 062 import org.deegree.io.datastore.schema.MappedFeaturePropertyType; 063 import org.deegree.io.datastore.schema.MappedFeatureType; 064 import org.deegree.io.datastore.schema.MappedGMLId; 065 import org.deegree.io.datastore.schema.MappedGMLSchema; 066 import org.deegree.io.datastore.schema.MappedGMLSchemaDocument; 067 import org.deegree.io.datastore.schema.MappedGeometryPropertyType; 068 import org.deegree.io.datastore.schema.MappedPropertyType; 069 import org.deegree.io.datastore.schema.MappedSimplePropertyType; 070 import org.deegree.io.datastore.schema.TableRelation; 071 import org.deegree.io.datastore.schema.content.MappingField; 072 import org.deegree.io.datastore.schema.content.MappingGeometryField; 073 import org.deegree.io.datastore.schema.content.SimpleContent; 074 import org.deegree.model.crs.UnknownCRSException; 075 import org.deegree.model.feature.schema.FeatureType; 076 import org.deegree.model.feature.schema.PropertyType; 077 import org.xml.sax.SAXException; 078 079 /** 080 * Abstract base class for DDL generation from annotated GML schema files. 081 * <p> 082 * This abstract base class only implements the functionality needed to retrieve the necessary tables and columns used 083 * in an annotated GML schema. DDL generation is dependent on the specific SQL backend to be used, so this is 084 * implemented in concrete extensions of this class. 085 * 086 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a> 087 * @author last edited by: $Author: mschneider $ 088 * 089 * @version $Revision: 8095 $, $Date: 2007-09-04 14:17:42 +0200 (Di, 04 Sep 2007) $ 090 */ 091 public abstract class DDLGenerator { 092 093 protected static final String FT_PREFIX = "FT_"; 094 095 protected static final int FEATURE_TYPE_TABLE = 0; 096 097 protected static final int JOIN_TABLE = 1; 098 099 protected static final int MULTI_PROPERTY_TABLE = 2; 100 101 protected MappedGMLSchema schema; 102 103 // key type: String (table names), value type: TableDefinition 104 protected Map<String, TableDefinition> tables = new HashMap<String, TableDefinition>(); 105 106 /** 107 * Creates a new instance of <code>DDLGenerator</code> from the given parameters. 108 * 109 * @param schemaURL 110 * @throws MalformedURLException 111 * @throws IOException 112 * @throws SAXException 113 * @throws XMLParsingException 114 * @throws XMLSchemaException 115 * @throws UnknownCRSException 116 */ 117 protected DDLGenerator( URL schemaURL ) throws MalformedURLException, IOException, SAXException, 118 XMLParsingException, XMLSchemaException, UnknownCRSException { 119 120 System.out.println( Messages.format( "LOADING_SCHEMA_FILE", schemaURL ) ); 121 MappedGMLSchemaDocument schemaDoc = new MappedGMLSchemaDocument(); 122 schemaDoc.load( schemaURL ); 123 schema = schemaDoc.parseMappedGMLSchema(); 124 FeatureType[] featureTypes = schema.getFeatureTypes(); 125 int concreteCount = 0; 126 for ( int i = 0; i < featureTypes.length; i++ ) { 127 if ( !featureTypes[i].isAbstract() ) { 128 concreteCount++; 129 } 130 } 131 System.out.println( Messages.format( "SCHEMA_INFO", new Integer( featureTypes.length ), 132 new Integer( featureTypes.length - concreteCount ), 133 new Integer( concreteCount ) ) ); 134 System.out.println( Messages.getString( "RETRIEVING_TABLES" ) ); 135 buildTableMap(); 136 } 137 138 /** 139 * Returns all table definitions of the given type. 140 * 141 * @param type 142 * FEATURE_TYPE_TABLE, JOIN_TABLE or MULTI_PROPERTY_TABLE 143 * @return all table definitions of the given type. 144 */ 145 protected TableDefinition[] getTables( int type ) { 146 Collection<TableDefinition> tableList = new ArrayList<TableDefinition>(); 147 Iterator iter = this.tables.keySet().iterator(); 148 while ( iter.hasNext() ) { 149 String tableName = (String) iter.next(); 150 TableDefinition table = this.tables.get( tableName ); 151 if ( table.getType() == type ) { 152 tableList.add( table ); 153 } 154 } 155 return tableList.toArray( new TableDefinition[tableList.size()] ); 156 } 157 158 /** 159 * Returns the table definition for the table with the given name. If no such definition exists, a new table 160 * definition is created and added to the internal <code>tables</code> map. 161 * 162 * @param tableName 163 * table definition to look up 164 * @param type 165 * type of the table (only respected, if a new TableDefinition instance is created) 166 * @return the table definition for the table with the given name. 167 */ 168 private TableDefinition lookupTableDefinition( String tableName, int type ) { 169 TableDefinition table = this.tables.get( tableName ); 170 if ( table == null ) { 171 table = new TableDefinition( tableName, type ); 172 this.tables.put( tableName, table ); 173 } 174 return table; 175 } 176 177 /** 178 * Collects the referenced tables and their columns from the input schema. Builds the member map <code>tables</code> 179 * from this data. 180 */ 181 private void buildTableMap() { 182 FeatureType[] featureTypes = schema.getFeatureTypes(); 183 for ( int i = 0; i < featureTypes.length; i++ ) { 184 if ( !featureTypes[i].isAbstract() ) { 185 buildTableMap( (MappedFeatureType) featureTypes[i] ); 186 } 187 } 188 } 189 190 /** 191 * Collects the tables and their columns used in the annotation of the given feature type. Builds the member map 192 * <code>tables</code> from this data. 193 * 194 * @param featureType 195 * feature type to process 196 */ 197 private void buildTableMap( MappedFeatureType featureType ) { 198 TableDefinition table = lookupTableDefinition( featureType.getTable(), FEATURE_TYPE_TABLE ); 199 200 addGMLIdColumns( featureType.getGMLId(), table ); 201 202 PropertyType[] properties = featureType.getProperties(); 203 for ( int i = 0; i < properties.length; i++ ) { 204 MappedPropertyType property = (MappedPropertyType) properties[i]; 205 if ( property instanceof MappedSimplePropertyType ) { 206 buildTableMap( (MappedSimplePropertyType) property, table ); 207 } else if ( property instanceof MappedGeometryPropertyType ) { 208 buildTableMap( (MappedGeometryPropertyType) property, table ); 209 } else if ( property instanceof MappedFeaturePropertyType ) { 210 buildTableMap( (MappedFeaturePropertyType) property, table ); 211 } else { 212 throw new RuntimeException( Messages.format( "ERROR_UNEXPECTED_PROPERTY_TYPE", 213 property.getClass().getName() ) ); 214 } 215 } 216 } 217 218 /** 219 * Adds the columns used in the given <code>MappedGMLId</code> to the also given <code>TableDefinition</code>. 220 * 221 * @param gmlId 222 * columns are taken from this gmlId mapping 223 * @param table 224 * columns are added to this table definition 225 */ 226 private void addGMLIdColumns( MappedGMLId gmlId, TableDefinition table ) { 227 MappingField[] idFields = gmlId.getIdFields(); 228 for ( int i = 0; i < idFields.length; i++ ) { 229 ColumnDefinition column = new ColumnDefinition( idFields[i].getField(), idFields[i].getType(), false, 230 false, -1 ); 231 table.addColumn( column ); 232 } 233 } 234 235 /** 236 * Collects the tables and their columns used in the annotation of the given simple property type. Builds the 237 * <code>table</code> member map from this data. 238 * <p> 239 * If the data for the property is stored in a related table, the table and column information used on the path to 240 * this table is also added to the <code>tables</code> member map. 241 * 242 * @param simpleProperty 243 * simple property type to process 244 * @param table 245 * table definition associated with the property definition 246 */ 247 private void buildTableMap( MappedSimplePropertyType simpleProperty, TableDefinition table ) { 248 Collection<ColumnDefinition> newColumns = new ArrayList<ColumnDefinition>(); 249 // array must always have length 1 250 TableRelation[] relations = simpleProperty.getTableRelations(); 251 if ( simpleProperty.getMaxOccurs() != 1 && ( relations == null || relations.length < 1 ) ) { 252 throw new RuntimeException( Messages.format( "ERROR_INVALID_PROPERTY_DEFINITION", simpleProperty.getName() ) ); 253 } 254 255 SimpleContent content = simpleProperty.getContent(); 256 if ( content instanceof MappingField ) { 257 MappingField mf = (MappingField) content; 258 if ( relations == null || relations.length == 0 ) { 259 newColumns.add( new ColumnDefinition( mf.getField(), mf.getType(), simpleProperty.getMinOccurs() == 0, 260 false, -1 ) ); 261 } else { 262 TableRelation firstRelation = relations[0]; 263 MappingField[] fromFields = firstRelation.getFromFields(); 264 for ( int i = 0; i < fromFields.length; i++ ) { 265 MappingField fromField = fromFields[i]; 266 newColumns.add( new ColumnDefinition( fromField.getField(), fromField.getType(), false, false, -1 ) ); 267 } 268 buildTableMap( relations, mf ); 269 } 270 } else { 271 String msg = "Ignoring property '" + simpleProperty + "' - has virtual content."; 272 System.out.println( msg ); 273 } 274 table.addColumns( newColumns ); 275 } 276 277 /** 278 * Collects the tables and their columns used in the annotation of the given geometry property type. Builds the 279 * <code>table</code> member map from this data. 280 * <p> 281 * If the geometry for the property is stored in a related table, the table and column information used on the path 282 * to this table is also added to the <code>tables</code> member map. 283 * 284 * @param geometryProperty 285 * feature property type to process 286 * @param table 287 * table definition associated with the property definition 288 */ 289 private void buildTableMap( MappedGeometryPropertyType geometryProperty, TableDefinition table ) { 290 Collection<ColumnDefinition> newColumns = new ArrayList<ColumnDefinition>(); 291 TableRelation[] relations = geometryProperty.getTableRelations(); 292 if ( geometryProperty.getMaxOccurs() != 1 && ( relations == null || relations.length < 1 ) ) { 293 throw new RuntimeException( Messages.format( "ERROR_INVALID_PROPERTY_DEFINITION", 294 geometryProperty.getName() ) ); 295 } 296 if ( relations == null || relations.length == 0 ) { 297 newColumns.add( new ColumnDefinition( geometryProperty.getMappingField().getField(), 298 geometryProperty.getMappingField().getType(), 299 geometryProperty.getMinOccurs() == 0, true, 300 geometryProperty.getMappingField().getSRS() ) ); 301 } else { 302 TableRelation firstRelation = relations[0]; 303 MappingField[] fromFields = firstRelation.getFromFields(); 304 for ( int i = 0; i < fromFields.length; i++ ) { 305 MappingField fromField = fromFields[i]; 306 newColumns.add( new ColumnDefinition( fromField.getField(), fromField.getType(), false, true, 307 geometryProperty.getMappingField().getSRS() ) ); 308 } 309 buildTableMap( relations, geometryProperty.getMappingField() ); 310 } 311 table.addColumns( newColumns ); 312 } 313 314 /** 315 * Collects the tables and their columns used in the annotation of the given feature property type. Builds the 316 * <code>table</code> member map from this data. 317 * <p> 318 * The table and column information used on the path to the table of the feature type is also added to the 319 * <code>tables</code> member map. 320 * 321 * @param featureProperty 322 * feature property type to process 323 * @param table 324 * table definition associated with the property definition 325 */ 326 private void buildTableMap( MappedFeaturePropertyType featureProperty, TableDefinition table ) { 327 Collection<ColumnDefinition> newColumns = new ArrayList<ColumnDefinition>(); 328 329 // array must always have length 1 330 TableRelation[] relations = featureProperty.getTableRelations(); 331 332 // target feature type table must always be accessed via 'Relation'-elements 333 if ( relations == null || relations.length < 1 ) { 334 throw new RuntimeException( Messages.format( "ERROR_INVALID_FEATURE_PROPERTY_DEFINITION_1", 335 featureProperty.getName() ) ); 336 } 337 338 // maxOccurs > 1: target feature type table must be accessed via join table 339 if ( featureProperty.getMaxOccurs() != 1 && ( relations.length < 2 ) ) { 340 throw new RuntimeException( Messages.format( "ERROR_INVALID_FEATURE_PROPERTY_DEFINITION_2", 341 featureProperty.getName() ) ); 342 } 343 344 // add this feature type's key columns to current table 345 TableRelation firstRelation = relations[0]; 346 MappingField[] fromFields = firstRelation.getFromFields(); 347 boolean isNullable = featureProperty.getMinOccurs() == 0 && relations.length == 1; 348 for ( int i = 0; i < fromFields.length; i++ ) { 349 MappingField fromField = fromFields[i]; 350 newColumns.add( new ColumnDefinition( fromField.getField(), fromField.getType(), isNullable, false, -1 ) ); 351 } 352 table.addColumns( newColumns ); 353 354 MappedFeatureType contentType = featureProperty.getFeatureTypeReference().getFeatureType(); 355 buildTableMap( relations, featureProperty, contentType ); 356 } 357 358 /** 359 * Collects the tables and their columns used in the relation tables from a simple/geometry property to it's content 360 * table. Builds the <code>table</code> member map from this data. 361 * 362 * @param relations 363 * relation tables from annotation of property type 364 * @param targetField 365 * holds the properties data 366 */ 367 private void buildTableMap( TableRelation[] relations, MappingField targetField ) { 368 369 // process tables used in 'To'-element of each 'Relation'-element 370 for ( int i = 0; i < relations.length; i++ ) { 371 String tableName = relations[i].getToTable(); 372 TableDefinition table = lookupTableDefinition( tableName, MULTI_PROPERTY_TABLE ); 373 MappingField[] toFields = relations[i].getToFields(); 374 for ( int j = 0; j < toFields.length; j++ ) { 375 ColumnDefinition column = new ColumnDefinition( toFields[j].getField(), toFields[j].getType(), false, 376 false, -1 ); 377 table.addColumn( column ); 378 } 379 } 380 381 // process table used in 'To'-element of last 'Relation'-element (targetField refers to 382 // this) 383 ColumnDefinition column = null; 384 if ( targetField instanceof MappingGeometryField ) { 385 386 column = new ColumnDefinition( targetField.getField(), targetField.getType(), false, true, 387 ( (MappingGeometryField) targetField ).getSRS() ); 388 } else { 389 column = new ColumnDefinition( targetField.getField(), targetField.getType(), false, false, -1 ); 390 } 391 392 TableDefinition table = lookupTableDefinition( relations[relations.length - 1].getToTable(), 393 MULTI_PROPERTY_TABLE ); 394 table.addColumn( column ); 395 } 396 397 /** 398 * Collects the tables and their columns used in the relation tables from a feature property to it's content feature 399 * type. Builds the <code>table</code> member map from this data. 400 * 401 * @param relations 402 * relation tables from annotation of feature property type 403 * @param property 404 * @param targetType 405 * type contained in the feature property 406 */ 407 private void buildTableMap( TableRelation[] relations, MappedPropertyType property, MappedFeatureType targetType ) { 408 409 TableDefinition table = lookupTableDefinition( relations[0].getFromTable(), FEATURE_TYPE_TABLE ); 410 411 // process tables used in 'To'-element of each 'Relation'-element (except the last) 412 for ( int i = 0; i < relations.length - 1; i++ ) { 413 String tableName = relations[i].getToTable(); 414 table = lookupTableDefinition( tableName, JOIN_TABLE ); 415 MappingField[] toFields = relations[i].getToFields(); 416 for ( int j = 0; j < toFields.length; j++ ) { 417 ColumnDefinition column = new ColumnDefinition( toFields[j].getField(), toFields[j].getType(), false, 418 false, -1 ); 419 table.addColumn( column ); 420 } 421 } 422 423 // process table used in 'To'-element of last 'Relation'-element 424 MappedFeatureType[] concreteTypes = targetType.getConcreteSubstitutions(); 425 MappingField[] toFields = relations[relations.length - 1].getToFields(); 426 427 // if it refers to several target tables (target feature type is abstract), an additional 428 // column is needed (which determines the target feature type) 429 if ( concreteTypes.length > 1 ) { 430 String typeColumn = "featuretype"; 431 if ( relations.length == 1 ) { 432 typeColumn = FT_PREFIX + property.getName().getLocalName().toUpperCase(); 433 } 434 ColumnDefinition column = new ColumnDefinition( typeColumn, Types.VARCHAR, property.getMinOccurs() == 0, 435 false, -1 ); 436 table.addColumn( column ); 437 } 438 for ( int i = 0; i < concreteTypes.length; i++ ) { 439 MappedFeatureType concreteType = concreteTypes[i]; 440 String tableName = concreteType.getTable(); 441 table = lookupTableDefinition( tableName, FEATURE_TYPE_TABLE ); 442 for ( int j = 0; j < toFields.length; j++ ) { 443 ColumnDefinition column = new ColumnDefinition( toFields[j].getField(), toFields[j].getType(), false, 444 false, -1 ); 445 table.addColumn( column ); 446 } 447 } 448 449 // process tables used in 'From'-element of each 'Relation'-element (except the first) 450 for ( int i = 1; i < relations.length; i++ ) { 451 String tableName = relations[i].getFromTable(); 452 if ( i != relations.length - 1 ) { 453 table = lookupTableDefinition( tableName, JOIN_TABLE ); 454 } else { 455 table = lookupTableDefinition( tableName, FEATURE_TYPE_TABLE ); 456 } 457 MappingField[] fromFields = relations[i].getFromFields(); 458 for ( int j = 0; j < fromFields.length; j++ ) { 459 ColumnDefinition column = new ColumnDefinition( fromFields[j].getField(), fromFields[j].getType(), 460 false, false, -1 ); 461 table.addColumn( column ); 462 } 463 } 464 } 465 466 /** 467 * Generates the DDL statements that can be used to build a relational schema that backs the GML schema. 468 * 469 * @param outputFile 470 * @throws IOException 471 */ 472 public void generateCreateScript( String outputFile ) 473 throws IOException { 474 PrintWriter writer = new PrintWriter( new FileWriter( outputFile ) ); 475 TableDefinition[] tables = getTables( FEATURE_TYPE_TABLE ); 476 System.out.println( Messages.format( "CREATE_FEATURE_TYPE", new Integer( tables.length ) ) ); 477 writer.println( "/* CREATE FEATURE TABLES (" + tables.length + ") */\n" ); 478 for ( int i = 0; i < tables.length; i++ ) { 479 writer.println( generateCreateStatements( tables[i] ) ); 480 } 481 tables = getTables( JOIN_TABLE ); 482 if ( tables.length != 0 ) { 483 writer.println( "/* CREATE JOIN TABLES (" + tables.length + ") */\n" ); 484 } 485 System.out.println( Messages.format( "CREATE_JOIN_TABLES", new Integer( tables.length ) ) ); 486 for ( int i = 0; i < tables.length; i++ ) { 487 writer.println( generateCreateStatements( tables[i] ) ); 488 } 489 tables = getTables( MULTI_PROPERTY_TABLE ); 490 if ( tables.length != 0 ) { 491 writer.println( "/* CREATE PROPERTY TABLES (" + tables.length + ") */\n" ); 492 } 493 System.out.println( Messages.format( "CREATE_PROPERTY_TABLES", new Integer( tables.length ) ) ); 494 for ( int i = 0; i < tables.length; i++ ) { 495 writer.println( generateCreateStatements( tables[i] ) ); 496 } 497 writer.close(); 498 } 499 500 /** 501 * Generates the DDL statements necessary for the creation of the given table definition. Must be overwritten by the 502 * concrete implementation. 503 * 504 * @param table 505 * @return the DDL statements necessary for the creation of the given table definition 506 */ 507 protected abstract StringBuffer generateCreateStatements( TableDefinition table ); 508 509 /** 510 * Generates the DDL statements that can be used to remove the relational schema again. 511 * 512 * @param outputFile 513 * @throws IOException 514 */ 515 public void generateDropScript( String outputFile ) 516 throws IOException { 517 PrintWriter writer = new PrintWriter( new FileWriter( outputFile ) ); 518 TableDefinition[] tables = getTables( FEATURE_TYPE_TABLE ); 519 System.out.println( Messages.format( "DROP_FEATURE_TYPE", new Integer( tables.length ) ) ); 520 writer.println( "/* DROP FEATURE TABLES (" + tables.length + ") */\n" ); 521 for ( int i = 0; i < tables.length; i++ ) { 522 writer.println( generateDropStatements( tables[i] ) ); 523 } 524 tables = getTables( JOIN_TABLE ); 525 writer.println( "/* DROP JOIN TABLES (" + tables.length + ") */\n" ); 526 System.out.println( Messages.format( "DROP_JOIN_TABLES", new Integer( tables.length ) ) ); 527 for ( int i = 0; i < tables.length; i++ ) { 528 writer.println( generateDropStatements( tables[i] ) ); 529 } 530 tables = getTables( MULTI_PROPERTY_TABLE ); 531 writer.println( "/* DROP PROPERTY TABLES (" + tables.length + ") */\n" ); 532 System.out.println( Messages.format( "DROP_PROPERTY_TABLES", new Integer( tables.length ) ) ); 533 for ( int i = 0; i < tables.length; i++ ) { 534 writer.println( generateDropStatements( tables[i] ) ); 535 } 536 writer.close(); 537 } 538 539 /** 540 * Generates the DDL statements necessary for the removal of the given table definition. Must be overwritten by the 541 * concrete implementation. 542 * 543 * @param table 544 * @return the DDL statements necessary for the removal of the given table definition 545 */ 546 protected abstract StringBuffer generateDropStatements( TableDefinition table ); 547 548 /** 549 * @param args 550 * @throws IOException 551 * @throws SAXException 552 * @throws XMLParsingException 553 * @throws XMLSchemaException 554 * @throws UnknownCRSException 555 */ 556 public static void main( String[] args ) 557 throws IOException, SAXException, XMLParsingException, XMLSchemaException, 558 UnknownCRSException { 559 560 if ( args.length != 4 ) { 561 System.out.println( "Usage: DDLGenerator [FLAVOUR] <input.xsd> <create.sql> <drop.sql>" ); 562 System.exit( 0 ); 563 } 564 565 String flavour = args[0]; 566 String schemaFile = args[1]; 567 String createFile = args[2]; 568 String dropFile = args[3]; 569 570 DDLGenerator generator = null; 571 if ( "POSTGIS".equals( flavour ) ) { 572 generator = new PostGISDDLGenerator( new File( schemaFile ).toURL() ); 573 } else { 574 System.out.println( Messages.format( "ERROR_UNSUPPORTED_FLAVOUR", flavour ) ); 575 } 576 577 generator.generateCreateScript( createFile ); 578 generator.generateDropScript( dropFile ); 579 } 580 581 /** 582 * Returns a string representation of the object. 583 * 584 * @return a string representation of the object. 585 */ 586 @Override 587 public String toString() { 588 StringBuffer sb = new StringBuffer( Messages.getString( "RELATIONAL_SCHEMA" ) ); 589 sb.append( '\n' ); 590 591 TableDefinition[] tables = getTables( FEATURE_TYPE_TABLE ); 592 sb.append( '\n' ); 593 sb.append( tables.length ); 594 sb.append( " feature type tables\n\n" ); 595 for ( int i = 0; i < tables.length; i++ ) { 596 sb.append( tables[i] ); 597 sb.append( '\n' ); 598 } 599 600 sb.append( '\n' ); 601 tables = getTables( JOIN_TABLE ); 602 sb.append( tables.length ); 603 sb.append( " join tables\n\n" ); 604 for ( int i = 0; i < tables.length; i++ ) { 605 sb.append( tables[i] ); 606 sb.append( '\n' ); 607 } 608 609 sb.append( '\n' ); 610 tables = getTables( MULTI_PROPERTY_TABLE ); 611 sb.append( tables.length ); 612 sb.append( " property tables\n\n" ); 613 for ( int i = 0; i < tables.length; i++ ) { 614 sb.append( tables[i] ); 615 sb.append( '\n' ); 616 } 617 return sb.toString(); 618 } 619 620 class TableDefinition { 621 622 private int type; 623 624 private String tableName; 625 626 private Map<String, ColumnDefinition> columnsMap = new LinkedHashMap<String, ColumnDefinition>(); 627 628 TableDefinition( String tableName, int type ) { 629 this.type = type; 630 this.tableName = tableName; 631 } 632 633 String getName() { 634 return this.tableName; 635 } 636 637 int getType() { 638 return this.type; 639 } 640 641 ColumnDefinition[] getColumns() { 642 Collection<ColumnDefinition> columns = new ArrayList<ColumnDefinition>(); 643 Iterator iter = columnsMap.keySet().iterator(); 644 while ( iter.hasNext() ) { 645 String columnName = (String) iter.next(); 646 columns.add( columnsMap.get( columnName ) ); 647 } 648 return columns.toArray( new ColumnDefinition[columns.size()] ); 649 } 650 651 ColumnDefinition[] getPKColumns() { 652 Collection<ColumnDefinition> columns = new ArrayList<ColumnDefinition>(); 653 Iterator iter = columnsMap.keySet().iterator(); 654 while ( iter.hasNext() ) { 655 String columnName = (String) iter.next(); 656 ColumnDefinition column = columnsMap.get( columnName ); 657 if ( column.isPartOfPK() ) { 658 columns.add( columnsMap.get( columnName ) ); 659 } 660 } 661 return columns.toArray( new ColumnDefinition[columns.size()] ); 662 } 663 664 ColumnDefinition getColumn( String name ) { 665 return columnsMap.get( name ); 666 } 667 668 void addColumn( ColumnDefinition column ) { 669 ColumnDefinition oldColumn = columnsMap.get( column.getName() ); 670 if ( oldColumn != null ) { 671 if ( !( column.getType() == oldColumn.getType() ) ) { 672 String msg = Messages.format( "ERROR_COLUMN_DEFINITION_TYPES", column.getName(), 673 oldColumn.isNullable() ? "NULLABLE" : "NOT NULLABLE", 674 column.isNullable() ? "NULLABLE" : "NOT NULLABLE" ); 675 throw new RuntimeException( msg ); 676 677 } 678 if ( oldColumn.isPartOfPK() ) { 679 column = oldColumn; 680 } 681 } 682 columnsMap.put( column.getName(), column ); 683 } 684 685 void addColumns( Collection columns ) { 686 Iterator iter = columns.iterator(); 687 while ( iter.hasNext() ) { 688 ColumnDefinition column = (ColumnDefinition) iter.next(); 689 addColumn( column ); 690 } 691 } 692 693 @Override 694 public String toString() { 695 StringBuffer sb = new StringBuffer(); 696 sb.append( Messages.format( "TABLE", this.tableName ) ); 697 sb.append( Messages.getString( "PRIMARY_KEY" ) ); 698 ColumnDefinition[] pkColumns = getPKColumns(); 699 for ( int i = 0; i < pkColumns.length; i++ ) { 700 sb.append( '"' ); 701 sb.append( pkColumns[i].getName() ); 702 sb.append( '"' ); 703 if ( i != pkColumns.length - 1 ) { 704 sb.append( ", " ); 705 } 706 } 707 sb.append( '\n' ); 708 Iterator columnNameIter = this.columnsMap.keySet().iterator(); 709 while ( columnNameIter.hasNext() ) { 710 String columnName = (String) columnNameIter.next(); 711 ColumnDefinition column = this.columnsMap.get( columnName ); 712 try { 713 sb.append( Messages.format( "COLUMN", columnName, 714 Types.getTypeNameForSQLTypeCode( column.getType() ) + ":" 715 + column.getType(), 716 new Boolean( column.isNullable() ) ) ); 717 } catch ( UnknownTypeException e ) { 718 // TODO Auto-generated catch block 719 e.printStackTrace(); 720 } 721 sb.append( '\n' ); 722 } 723 return sb.toString(); 724 } 725 } 726 727 class ColumnDefinition { 728 729 private String columnName; 730 731 private int type; 732 733 private boolean isNullable; 734 735 private boolean isGeometryColumn; 736 737 private int srsCode; 738 739 private boolean isPartOfPK; 740 741 ColumnDefinition( String columnName, int type, boolean isNullable, boolean isGeometryColumn, int srsCode ) { 742 this.columnName = columnName; 743 this.type = type; 744 this.isNullable = isNullable; 745 this.isGeometryColumn = isGeometryColumn; 746 this.srsCode = srsCode; 747 } 748 749 ColumnDefinition( String columnName, int type, boolean isNullable, boolean isPartOfPK, 750 boolean isGeometryColumn, int srsCode ) { 751 this( columnName, type, isNullable, isGeometryColumn, srsCode ); 752 this.isPartOfPK = isPartOfPK; 753 } 754 755 String getName() { 756 return this.columnName; 757 } 758 759 int getType() { 760 return this.type; 761 } 762 763 boolean isNullable() { 764 return this.isNullable; 765 } 766 767 boolean isGeometry() { 768 return this.isGeometryColumn; 769 } 770 771 int getSRS() { 772 return this.srsCode; 773 } 774 775 boolean isPartOfPK() { 776 return this.isPartOfPK; 777 } 778 } 779 }