001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/datastore/schema/MappedGMLSchema.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 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.io.datastore.schema; 044 045 import java.net.URI; 046 import java.util.Iterator; 047 import java.util.List; 048 import java.util.Properties; 049 050 import org.deegree.datatypes.QualifiedName; 051 import org.deegree.datatypes.Types; 052 import org.deegree.framework.log.ILogger; 053 import org.deegree.framework.log.LoggerFactory; 054 import org.deegree.framework.xml.XMLParsingException; 055 import org.deegree.framework.xml.schema.ComplexTypeDeclaration; 056 import org.deegree.framework.xml.schema.ElementDeclaration; 057 import org.deegree.framework.xml.schema.SimpleTypeDeclaration; 058 import org.deegree.framework.xml.schema.XMLSchemaException; 059 import org.deegree.io.datastore.Datastore; 060 import org.deegree.io.datastore.DatastoreConfiguration; 061 import org.deegree.io.datastore.DatastoreException; 062 import org.deegree.io.datastore.DatastoreRegistry; 063 import org.deegree.io.datastore.idgenerator.IdGenerator; 064 import org.deegree.io.datastore.schema.MappedGMLId.IDPART_INFO; 065 import org.deegree.io.datastore.schema.content.MappingField; 066 import org.deegree.io.datastore.schema.content.MappingGeometryField; 067 import org.deegree.model.crs.CRSFactory; 068 import org.deegree.model.crs.CoordinateSystem; 069 import org.deegree.model.crs.UnknownCRSException; 070 import org.deegree.model.feature.schema.AbstractPropertyType; 071 import org.deegree.model.feature.schema.FeatureType; 072 import org.deegree.model.feature.schema.GMLSchema; 073 import org.deegree.model.feature.schema.PropertyType; 074 import org.deegree.model.feature.schema.UndefinedFeatureTypeException; 075 import org.deegree.ogcbase.CommonNamespaces; 076 import org.w3c.dom.Element; 077 078 /** 079 * Represents a GML application schema document which is annotated with mapping (persistence) 080 * information. 081 * 082 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> 083 * @author last edited by: $Author: apoth $ 084 * 085 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $ 086 */ 087 public class MappedGMLSchema extends GMLSchema { 088 089 private final static ILogger LOG = LoggerFactory.getLogger( MappedGMLSchema.class ); 090 091 private static URI XSDNS = CommonNamespaces.XSNS; 092 093 private MappedGMLSchemaDocument doc; 094 095 private Datastore datastore; 096 097 private boolean suppressXLinkOutput; 098 099 private String namespacePrefix; 100 101 private URI defaultSRS; 102 103 private CoordinateSystem defaultCS; 104 105 // TODO remove this hack (which is used to mark the first feature type as visible by default) 106 private boolean firstFeatureType = true; 107 108 /** 109 * Creates a new <code>MappedGMLSchema</code> instance from the given parameters. 110 * 111 * @param targetNamespace 112 * @param simpleTypes 113 * @param complexTypes 114 * @param elementDeclarations 115 * @param namespacePrefix 116 * @param defaultSRS 117 * @param backendConfiguration 118 * @param suppressXLinkOutput 119 * @param doc 120 * @throws XMLParsingException 121 * @throws UnknownCRSException 122 * @throws XMLSchemaException 123 */ 124 MappedGMLSchema( URI targetNamespace, SimpleTypeDeclaration[] simpleTypes, ComplexTypeDeclaration[] complexTypes, 125 ElementDeclaration[] elementDeclarations, String namespacePrefix, URI defaultSRS, 126 DatastoreConfiguration backendConfiguration, boolean suppressXLinkOutput, 127 MappedGMLSchemaDocument doc ) throws XMLParsingException, UnknownCRSException { 128 129 super( elementDeclarations, targetNamespace, simpleTypes, complexTypes ); 130 131 this.doc = doc; 132 this.namespacePrefix = namespacePrefix; 133 this.defaultSRS = defaultSRS; 134 this.defaultCS = CRSFactory.create( defaultSRS.toString() ); 135 this.datastore = registerDatastore( backendConfiguration ); 136 this.suppressXLinkOutput = suppressXLinkOutput; 137 138 buildFeatureTypeMap( elementDeclarations ); 139 buildSubstitutionMap( elementDeclarations ); 140 resolveFeatureTypeReferences(); 141 resolveTargetTables(); 142 checkIdentityPartConsistency(); 143 144 try { 145 this.datastore.bindSchema( this ); 146 } catch ( DatastoreException e ) { 147 LOG.logError( e.getMessage(), e ); 148 throw new XMLParsingException( e.getMessage() ); 149 } 150 } 151 152 /** 153 * Checks for all feature type definitions if it's featureIds 'identityPart' setting is valid: 154 * <ul> 155 * <li>if there is a direct fk from the feature's table to another feature table, 156 * 'identityPart' must be true</li> 157 * <li>if there is no explicit setting for the feature type, the implied setting is used, 158 * otherwise it is checked for validity</li> 159 * </ul> 160 * 161 * @throws XMLSchemaException 162 */ 163 private void checkIdentityPartConsistency() 164 throws XMLSchemaException { 165 for ( FeatureType ft : this.featureTypeMap.values() ) { 166 MappedFeatureType mft = (MappedFeatureType) ft; 167 PropertyType[] properties = mft.getProperties(); 168 for ( int i = 0; i < properties.length; i++ ) { 169 MappedPropertyType property = (MappedPropertyType) properties[i]; 170 if ( property instanceof MappedFeaturePropertyType ) { 171 MappedFeaturePropertyType featurePT = (MappedFeaturePropertyType) property; 172 TableRelation[] relations = featurePT.getTableRelations(); 173 if ( relations.length == 1 ) { 174 if ( relations[0].getFKInfo() == TableRelation.FK_INFO.fkIsToField ) { 175 MappedFeatureType targetFT = featurePT.getFeatureTypeReference().getFeatureType(); 176 MappedGMLId id = targetFT.getGMLId(); 177 if ( id.getIdPartInfo() == IDPART_INFO.noIDInfo ) { 178 String msg = "FeatureId for feature type '" + targetFT.getName() 179 + "' has to be part of the feature's identity - feature table " 180 + "is a property of feature type '" + mft.getName() + "' and stores a fk."; 181 LOG.logInfo( msg ); 182 } else if ( id.getIdPartInfo() == IDPART_INFO.notIDPart ) { 183 String msg = "Invalid schema annotation: " + "FeatureId for feature type '" 184 + targetFT.getName() 185 + "' has to be part of the feature's identity - feature table " 186 + "is a property of feature type '" + mft.getName() 187 + "' and stores a fk. Set 'identityPart' to true for " + "feature type '" 188 + targetFT.getName() + "'."; 189 throw new XMLSchemaException( msg ); 190 } 191 id.setIdentityPart( true ); 192 } 193 } 194 } 195 } 196 } 197 } 198 199 /** 200 * Retrieves a <code>Datastore</code> instance for the given configuration. 201 * <p> 202 * If a datastore with exactly the same configuration exists, the existing instance is returned. 203 * 204 * @param backendConfiguration 205 * @throws XMLSchemaException 206 */ 207 private Datastore registerDatastore( DatastoreConfiguration backendConfiguration ) 208 throws XMLSchemaException { 209 Datastore datastore = DatastoreRegistry.getDatastore( backendConfiguration ); 210 if ( datastore == null ) { 211 try { 212 datastore = (Datastore) backendConfiguration.getDatastoreClass().newInstance(); 213 datastore.configure( backendConfiguration ); 214 } catch ( DatastoreException e ) { 215 String msg = "Error configuring datastore with configuration '" + backendConfiguration + "'."; 216 LOG.logError( msg, e ); 217 throw new XMLSchemaException( msg, e ); 218 } catch ( Exception e ) { 219 String msg = "Error instantiating datastore for class '" + backendConfiguration.getDatastoreClass() 220 + "'."; 221 LOG.logError( msg, e ); 222 throw new XMLSchemaException( msg, e ); 223 } 224 try { 225 DatastoreRegistry.registerDatastore( datastore ); 226 } catch ( DatastoreException e ) { 227 String msg = "Error registering datastore with configuration '" + backendConfiguration + "'."; 228 LOG.logError( msg, e ); 229 throw new XMLSchemaException( msg, e ); 230 } 231 } 232 return datastore; 233 } 234 235 /** 236 * Returns the underlying GML Application Schema document. 237 * 238 * @return the underlying GML Application Schema document 239 */ 240 public MappedGMLSchemaDocument getDocument() { 241 return this.doc; 242 } 243 244 /** 245 * Returns the {@link Datastore} instance that handles this schema. 246 * 247 * @return the Datastore instance that handles this schema 248 */ 249 public Datastore getDatastore() { 250 return this.datastore; 251 } 252 253 /** 254 * Returns whether GML output (of the associated datastore) will not use any XLinks. 255 * 256 * @return true, if the GML output will not use XLinks, false otherwise 257 */ 258 public boolean suppressXLinkOutput() { 259 return this.suppressXLinkOutput; 260 } 261 262 /** 263 * Returns the default SRS for all geometry properties in the schema. 264 * 265 * @return the default SRS for all geometry properties in the schema 266 */ 267 public URI getDefaultSRS() { 268 return this.defaultSRS; 269 } 270 271 /** 272 * Returns the default {@link CoordinateSystem} for all geometry properties in the schema. 273 * 274 * @return the default CoordinateSystem for all geometry properties in the schema 275 */ 276 public CoordinateSystem getDefaultCS() { 277 return this.defaultCS; 278 } 279 280 /** 281 * Looks up the <code>FeatureType</code> with the given <code>QualifiedName</code>. 282 * 283 * @param qName 284 * the QualifiedName to look up 285 * @return the FeatureType, if it is defined in the document, null otherwise 286 */ 287 @Override 288 public MappedFeatureType getFeatureType( QualifiedName qName ) { 289 return (MappedFeatureType) this.featureTypeMap.get( qName ); 290 } 291 292 /** 293 * Looks up the <code>FeatureType</code> with the given name. 294 * 295 * @param localName 296 * the name to look up 297 * @return the FeatureType, if it is defined in the document, null otherwise 298 */ 299 @Override 300 public MappedFeatureType getFeatureType( String localName ) { 301 return getFeatureType( new QualifiedName( localName, getTargetNamespace() ) ); 302 } 303 304 /** 305 * Builds a {@link MappedFeatureType} from the given element declaration. 306 * 307 * @param element 308 * @return feature type with persistence information 309 * @throws XMLParsingException 310 * @throws UnknownCRSException 311 */ 312 @Override 313 protected MappedFeatureType buildFeatureType( ElementDeclaration element ) 314 throws XMLParsingException, UnknownCRSException { 315 316 LOG.logDebug( "Building (mapped) feature type from element declaration '" + element.getName() + "'..." ); 317 318 int visibleCode = -1; 319 boolean isVisible = false; 320 boolean isUpdatable = false; 321 boolean isDeletable = false; 322 boolean isInsertable = false; 323 QualifiedName name = new QualifiedName( this.namespacePrefix, element.getName().getLocalName(), 324 getTargetNamespace() ); 325 MappedComplexTypeDeclaration complexType = (MappedComplexTypeDeclaration) element.getType().getTypeDeclaration(); 326 327 // extract mapping information from element annotation 328 Element annotationElement = ( (MappedElementDeclaration) element ).getAnnotation(); 329 MappedGMLId gmlId = null; 330 String table = name.getLocalName().toLowerCase(); 331 // use complexType annotation, if no element annotation present 332 if ( annotationElement == null ) { 333 annotationElement = complexType.getAnnotation(); 334 } 335 // neither element nor complexType annotation, then use default mapping 336 if ( annotationElement == null ) { 337 LOG.logInfo( "Declaration of feature type '" + name 338 + "' has no mapping information (annotation element). Defaulting to " + "table name '" + table 339 + "' and gmlId field 'fid' (not identity part)." ); 340 MappingField[] idFields = new MappingField[] { new MappingField( table, "fid", Types.VARCHAR ) }; 341 IdGenerator idGenerator = IdGenerator.getInstance( IdGenerator.TYPE_UUID, new Properties() ); 342 gmlId = new MappedGMLId( name.getLocalName().toUpperCase(), "_", idFields, idGenerator, 343 IDPART_INFO.noIDInfo ); 344 } else { 345 gmlId = doc.extractGMLId( annotationElement, table ); 346 table = gmlId.getIdFields()[0].getTable(); 347 try { 348 visibleCode = doc.parseVisible( annotationElement ); 349 isUpdatable = doc.parseIsUpdatable( annotationElement ); 350 isDeletable = doc.parseIsDeletable( annotationElement ); 351 isInsertable = doc.parseIsInsertable( annotationElement ); 352 } catch ( XMLParsingException e ) { 353 throw new XMLSchemaException( e ); 354 } 355 } 356 357 ElementDeclaration[] subElements = complexType.getElements(); 358 PropertyType[] properties = new PropertyType[subElements.length]; 359 for ( int i = 0; i < subElements.length; i++ ) { 360 MappedElementDeclaration subElement = (MappedElementDeclaration) subElements[i]; 361 properties[i] = buildPropertyType( subElement, table ); 362 } 363 364 // default visibility for first feature type is true, for all others it's false 365 if ( this.firstFeatureType ) { 366 isVisible = true; 367 if ( visibleCode == 0 ) { 368 isVisible = false; 369 } 370 this.firstFeatureType = false; 371 } else { 372 if ( visibleCode == 1 ) { 373 isVisible = true; 374 } 375 } 376 377 return new MappedFeatureType( name, element.isAbstract(), properties, table, gmlId, this, isVisible, 378 isUpdatable, isDeletable, isInsertable ); 379 } 380 381 protected PropertyType buildPropertyType( MappedElementDeclaration element, String table ) 382 throws XMLParsingException, UnknownCRSException { 383 384 AbstractPropertyType propertyType; 385 QualifiedName propertyName = new QualifiedName( this.namespacePrefix, element.getName().getLocalName(), 386 getTargetNamespace() ); 387 388 int minOccurs = element.getMinOccurs(); 389 int maxOccurs = element.getMaxOccurs(); 390 391 QualifiedName typeName = element.getType().getName(); 392 LOG.logDebug( "Building (mapped) property type from element declaration '" + propertyName + "', type='" 393 + typeName + "'..." ); 394 int type = determinePropertyType( element ); 395 396 // extract mapping annotation 397 Element annotationElement = element.getAnnotation(); 398 399 // get identityPart information from annotation 400 int identityCode = -1; 401 if ( annotationElement != null ) { 402 identityCode = doc.parseIdentityPart( annotationElement ); 403 } 404 405 if ( typeName.isInNamespace( XSDNS ) ) { 406 // simple property (basic xsd type) 407 if ( annotationElement == null ) { 408 LOG.logDebug( "Using default mapping for property type '" + propertyName + "'." ); 409 String field = propertyName.getLocalName().toLowerCase(); 410 int typeCode = getDefaultSQLTypeForXSDType( typeName ); 411 MappingField mappingField = new MappingField( table, field, typeCode ); 412 propertyType = new MappedSimplePropertyType( propertyName, type, minOccurs, maxOccurs, true, 413 new TableRelation[0], mappingField ); 414 } else { 415 LOG.logDebug( "Parsing mapping information for simple property type." ); 416 boolean isIdentityPart = identityCode == 0 ? false : true; 417 propertyType = doc.parseMappedSimplePropertyType( annotationElement, propertyName, type, minOccurs, 418 maxOccurs, isIdentityPart, table ); 419 } 420 } else { 421 switch ( type ) { 422 case Types.GEOMETRY: { 423 // geometry property 424 if ( annotationElement == null ) { 425 LOG.logDebug( "Using default mapping for property type '" + propertyName + "'." ); 426 String field = propertyName.getLocalName().toLowerCase(); 427 MappingGeometryField mappingField = new MappingGeometryField( table, field, Types.OTHER, -1 ); 428 propertyType = new MappedGeometryPropertyType( propertyName, typeName, type, minOccurs, maxOccurs, 429 false, this.defaultSRS, new TableRelation[0], 430 mappingField ); 431 } else { 432 LOG.logDebug( "Parsing mapping information for geometry property type." ); 433 boolean isIdentityPart = identityCode == 1 ? true : false; 434 propertyType = doc.parseMappedGeometryPropertyType( annotationElement, propertyName, typeName, 435 type, minOccurs, maxOccurs, isIdentityPart, 436 table ); 437 } 438 break; 439 } 440 case Types.FEATURE: { 441 // feature property 442 if ( annotationElement == null ) { 443 String msg = "Declaration of property type '" + propertyName 444 + "' has no mapping information (annotation element missing)."; 445 throw new XMLSchemaException( msg ); 446 } 447 LOG.logDebug( "Parsing mapping information for feature property type." ); 448 boolean isIdentityPart = identityCode == 0 ? false : true; 449 boolean isReferenceType = "ReferenceType".equals( typeName.getLocalName() ); 450 propertyType = doc.parseMappedFeaturePropertyType( annotationElement, propertyName, minOccurs, 451 maxOccurs, isIdentityPart, table, isReferenceType ); 452 break; 453 } 454 default: { 455 // no known namespace -> assume simple property with user defined simple type 456 // TODO check for inherited types 457 458 if ( annotationElement == null ) { 459 LOG.logDebug( "Using default mapping for property type '" + propertyName + "'." ); 460 String field = propertyName.getLocalName().toLowerCase(); 461 int typeCode = getDefaultSQLTypeForXSDType( typeName ); 462 MappingField mappingField = new MappingField( table, field, typeCode ); 463 propertyType = new MappedSimplePropertyType( propertyName, type, minOccurs, maxOccurs, true, 464 new TableRelation[0], mappingField ); 465 } else { 466 LOG.logDebug( "Parsing mapping information for simple property type." ); 467 boolean isIdentityPart = identityCode == 0 ? false : true; 468 propertyType = doc.parseMappedSimplePropertyType( annotationElement, propertyName, type, minOccurs, 469 maxOccurs, isIdentityPart, table ); 470 } 471 } 472 } 473 } 474 return propertyType; 475 } 476 477 /** 478 * @throws XMLSchemaException 479 */ 480 private void resolveTargetTables() 481 throws XMLSchemaException { 482 LOG.logDebug( "Resolving unspecified (null) table references for all FeaturePropertyTypes." ); 483 Iterator iter = featureTypeMap.values().iterator(); 484 while ( iter.hasNext() ) { 485 resolveTargetTables( (MappedFeatureType) iter.next() ); 486 } 487 } 488 489 private void resolveTargetTables( MappedFeatureType type ) 490 throws XMLSchemaException { 491 PropertyType[] properties = type.getProperties(); 492 for ( int i = 0; i < properties.length; i++ ) { 493 MappedPropertyType property = (MappedPropertyType) properties[i]; 494 if ( property instanceof MappedFeaturePropertyType ) { 495 resolveTargetTables( (MappedFeaturePropertyType) property ); 496 } 497 } 498 } 499 500 private void resolveTargetTables( MappedFeaturePropertyType featurePT ) 501 throws XMLSchemaException { 502 MappedFeatureType targetFeatureType = featurePT.getFeatureTypeReference().getFeatureType(); 503 if ( !targetFeatureType.isAbstract() ) { 504 TableRelation[] tableRelations = featurePT.getTableRelations(); 505 if ( tableRelations.length == 0 ) { 506 String msg = "Invalid feature property mapping '" + featurePT.getName() 507 + ": no relation elements - feature properties cannot be embedded in " 508 + "feature tables directly, but must use key relations to reference " + "subfeatures."; 509 LOG.logError( msg ); 510 throw new XMLSchemaException( msg ); 511 } 512 TableRelation lastRelation = tableRelations[tableRelations.length - 1]; 513 MappingField[] targetFields = lastRelation.getToFields(); 514 for ( int i = 0; i < targetFields.length; i++ ) { 515 String table = targetFields[i].getTable(); 516 if ( table != null ) { 517 if ( !targetFeatureType.getTable().equals( table ) ) { 518 String msg = "Invalid feature property mapping: type '" + targetFeatureType.getName() 519 + "' is bound to table '" + targetFeatureType.getTable() 520 + "', but last table relation specifies table '" + table + "'."; 521 LOG.logError( msg ); 522 throw new XMLSchemaException( msg ); 523 } 524 } 525 targetFields[i].setTable( targetFeatureType.getTable() ); 526 } 527 } 528 } 529 530 private void resolveFeatureTypeReferences() 531 throws UndefinedFeatureTypeException { 532 LOG.logDebug( "Resolving (mapped) FeatureType references for namespace '" + getTargetNamespace() + "'." ); 533 Iterator iter = featureTypeMap.values().iterator(); 534 while ( iter.hasNext() ) { 535 resolveFeatureTypeReferences( (MappedFeatureType) iter.next() ); 536 } 537 } 538 539 private void resolveFeatureTypeReferences( MappedFeatureType featureType ) 540 throws UndefinedFeatureTypeException { 541 LOG.logDebug( "Resolving (mapped) FeatureType references in definition of FeatureType '" 542 + featureType.getName() + "'." ); 543 PropertyType[] properties = featureType.getProperties(); 544 for ( int i = 0; i < properties.length; i++ ) { 545 if ( properties[i] instanceof MappedFeaturePropertyType ) { 546 MappedFeaturePropertyType featurePT = (MappedFeaturePropertyType) properties[i]; 547 resolveFeatureTypeReferences( featurePT.getFeatureTypeReference() ); 548 } 549 } 550 } 551 552 private void resolveFeatureTypeReferences( MappedFeatureTypeReference reference ) 553 throws UndefinedFeatureTypeException { 554 LOG.logDebug( "Resolving (mapped) FeatureType references to FeatureType '" + reference.getName() + "'." ); 555 if ( reference.isResolved() ) { 556 LOG.logDebug( "Already resolved." ); 557 } else { 558 MappedFeatureType featureType = getFeatureType( reference.getName() ); 559 if ( featureType == null ) { 560 String msg = "Reference to feature type '" + reference.getName() 561 + "' in mapping annotation can not be resolved."; 562 LOG.logDebug( msg ); 563 throw new UndefinedFeatureTypeException( msg ); 564 } 565 reference.resolve( featureType ); 566 resolveFeatureTypeReferences( featureType ); 567 } 568 } 569 570 /** 571 * Returns all non-abstract implementations of a given feature type that are defined in this 572 * schema. 573 * 574 * @param ft 575 * must be a <code>MappedFeatureType</code> 576 * @return all non-abstract implementations of the feature type 577 */ 578 @Override 579 public MappedFeatureType[] getSubstitutions( FeatureType ft ) { 580 MappedFeatureType[] substitutions = new MappedFeatureType[0]; 581 List<FeatureType> featureTypeList = this.substitutionMap.get( ft ); 582 if ( featureTypeList != null ) { 583 substitutions = featureTypeList.toArray( new MappedFeatureType[featureTypeList.size()] ); 584 } 585 return substitutions; 586 } 587 588 // TODO: implement this 589 private int getDefaultSQLTypeForXSDType( @SuppressWarnings("unused") 590 QualifiedName xsdTypeName ) { 591 return -1; 592 } 593 594 @Override 595 public String toString() { 596 StringBuffer sb = new StringBuffer( "GML schema targetNamespace='" ); 597 sb.append( getTargetNamespace() ); 598 sb.append( "'\n" ); 599 sb.append( "\n*** " ); 600 sb.append( featureTypeMap.size() ); 601 sb.append( " feature type declarations ***\n" ); 602 Iterator featureTypeIter = featureTypeMap.values().iterator(); 603 while ( featureTypeIter.hasNext() ) { 604 MappedFeatureType featureType = (MappedFeatureType) featureTypeIter.next(); 605 sb.append( featureTypeToString( featureType ) ); 606 if ( featureTypeIter.hasNext() ) { 607 sb.append( "\n\n" ); 608 } 609 } 610 return sb.toString(); 611 } 612 613 private String featureTypeToString( MappedFeatureType ft ) { 614 StringBuffer sb = new StringBuffer( "- " ); 615 if ( ft.isAbstract() ) { 616 sb.append( "(abstract) " ); 617 } 618 sb.append( "Feature type '" ); 619 sb.append( ft.getName() ); 620 sb.append( "' -> TABLE: '" ); 621 sb.append( ft.getTable() + "'" ); 622 if ( ft.isUpdatable() ) { 623 sb.append( " updatable" ); 624 } 625 if ( ft.isDeletable() ) { 626 sb.append( " deletable" ); 627 } 628 if ( ft.isInsertable() ) { 629 sb.append( " insertable" ); 630 } 631 sb.append( '\n' ); 632 PropertyType[] properties = ft.getProperties(); 633 for ( int i = 0; i < properties.length; i++ ) { 634 sb.append( " + '" ); 635 sb.append( properties[i].getName() ); 636 sb.append( "', type: " ); 637 sb.append( properties[i].getType() ); 638 sb.append( ", minOccurs: " ); 639 sb.append( properties[i].getMinOccurs() ); 640 sb.append( ", maxOccurs: " ); 641 sb.append( properties[i].getMaxOccurs() ); 642 sb.append( " -> " ); 643 // sb.append( ( (MappedPropertyType) properties[i] ).getContents()[0] ); 644 if ( i != properties.length - 1 ) { 645 sb.append( "\n" ); 646 } 647 } 648 return sb.toString(); 649 } 650 }