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