001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }