001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/datastore/sql/wherebuilder/QueryTableTree.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.sql.wherebuilder;
044
045 import java.util.ArrayList;
046 import java.util.HashMap;
047 import java.util.List;
048 import java.util.Map;
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.i18n.Messages;
055 import org.deegree.io.datastore.PropertyPathResolvingException;
056 import org.deegree.io.datastore.schema.MappedFeaturePropertyType;
057 import org.deegree.io.datastore.schema.MappedFeatureType;
058 import org.deegree.io.datastore.schema.MappedGMLId;
059 import org.deegree.io.datastore.schema.MappedGMLSchema;
060 import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
061 import org.deegree.io.datastore.schema.MappedPropertyType;
062 import org.deegree.io.datastore.schema.MappedSimplePropertyType;
063 import org.deegree.io.datastore.schema.TableRelation;
064 import org.deegree.io.datastore.schema.content.ConstantContent;
065 import org.deegree.io.datastore.schema.content.SimpleContent;
066 import org.deegree.io.datastore.sql.TableAliasGenerator;
067 import org.deegree.ogcbase.AttributeStep;
068 import org.deegree.ogcbase.CommonNamespaces;
069 import org.deegree.ogcbase.PropertyPath;
070 import org.deegree.ogcbase.PropertyPathStep;
071
072 /**
073 * Represents selected {@link MappedFeatureType}s and {@link PropertyPath} instances (properties used in an OGC filter
074 * and as sort criteria) and their mapping to a certain relational schema.
075 * <p>
076 * The requested {@link MappedFeatureType}s are the root nodes of the tree. If there is more than root node (feature
077 * type), the query requests a join between feature types.
078 *
079 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
080 * @author last edited by: $Author: apoth $
081 *
082 * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
083 */
084 public class QueryTableTree {
085
086 private static final ILogger LOG = LoggerFactory.getLogger( QueryTableTree.class );
087
088 private TableAliasGenerator aliasGenerator;
089
090 private FeatureTypeNode[] roots;
091
092 // maps the aliases (specified in the query) to the corresponding FeatureTypeNode
093 private Map<String, FeatureTypeNode> ftAliasToRootFtNode = new HashMap<String, FeatureTypeNode>();
094
095 // uses 2 lists instead of Map, because PropertyPath.equals() is overwritten,
096 // and identity (==) is needed here (different occurences of "equal" PropertyName
097 // in filter must be treated as different PropertyPaths)
098 private List<PropertyPath> propertyPaths = new ArrayList<PropertyPath>();
099
100 private List<PropertyNode> propertyNodes = new ArrayList<PropertyNode>();
101
102 /**
103 * Creates a new instance of <code>QueryTableTree</code>.
104 *
105 * @param rootFts
106 * selected feature types, more than one type means that the types are joined
107 * @param aliases
108 * aliases for the feature types, may be null (must have same length as rootFts otherwise)
109 * @param aliasGenerator
110 * aliasGenerator to be used to generate table aliases, may be null
111 */
112 public QueryTableTree( MappedFeatureType[] rootFts, String[] aliases, TableAliasGenerator aliasGenerator ) {
113
114 if ( aliasGenerator != null ) {
115 this.aliasGenerator = aliasGenerator;
116 } else {
117 this.aliasGenerator = new TableAliasGenerator();
118 }
119 this.roots = new FeatureTypeNode[rootFts.length];
120 for ( int i = 0; i < rootFts.length; i++ ) {
121 FeatureTypeNode rootFtNode = new FeatureTypeNode( rootFts[i], aliases != null ? aliases[i] : null,
122 aliasGenerator.generateUniqueAlias() );
123 this.roots[i] = rootFtNode;
124 if ( aliases != null ) {
125 this.ftAliasToRootFtNode.put( aliases[i], rootFtNode );
126 LOG.logDebug( "Alias '" + aliases[i] + "' maps to '" + rootFtNode + "'." );
127 }
128 }
129 }
130
131 /**
132 * Returns the root feature type nodes of the tree.
133 *
134 * @return the root feature type nodes of the tree, contains at least one entry
135 */
136 public FeatureTypeNode[] getRootNodes() {
137 return this.roots;
138 }
139
140 /**
141 * Returns the alias for the root table.
142 *
143 * TODO support more than one root node
144 *
145 * @return the alias for the root table
146 */
147 public String getRootAlias() {
148 return this.roots[0].getTableAlias();
149 }
150
151 /**
152 * Returns the property node for the given property path.
153 *
154 * @param path
155 * property to be looked up
156 * @return the property node for the given property path
157 */
158 public PropertyNode getPropertyNode( PropertyPath path ) {
159
160 PropertyNode node = null;
161 for ( int i = 0; i < this.propertyPaths.size(); i++ ) {
162 if ( this.propertyPaths.get( i ) == path ) {
163 node = this.propertyNodes.get( i );
164 break;
165 }
166 }
167 return node;
168 }
169
170 /**
171 * Tries to insert the given {@link PropertyPath} as a filter criterion into the tree.
172 * <p>
173 * The {@link PropertyPath} is validated during insertion.
174 *
175 * @param property
176 * property to be inserted, has to have at least one step
177 * @throws PropertyPathResolvingException
178 * if the path violates the feature type's schema
179 */
180 public void addFilterProperty( PropertyPath property )
181 throws PropertyPathResolvingException {
182 MappedPropertyType pt = validate( property, false );
183 if ( pt instanceof MappedSimplePropertyType ) {
184 SimpleContent content = ( (MappedSimplePropertyType) pt ).getContent();
185 if ( content instanceof ConstantContent ) {
186 // add SimplePropertyNode to root node (because table path is irrelevant)
187 String[] tableAliases = generateTableAliases( pt );
188 PropertyNode propertyNode = new SimplePropertyNode( (MappedSimplePropertyType) pt, roots[0],
189 tableAliases );
190 this.propertyPaths.add( property );
191 this.propertyNodes.add( propertyNode );
192 // root.addPropertyNode( propertyNode );
193 } else {
194 insertValidatedPath( property );
195 }
196 } else {
197 insertValidatedPath( property );
198 }
199 }
200
201 /**
202 * Tries to insert the given {@link PropertyPath} as a sort criterion into the tree.
203 * <p>
204 * The {@link PropertyPath} is validated during insertion. It is also checked that the path is unique, i.e. every
205 * property type on the path must have maxOccurs set to 1.
206 *
207 * @param property
208 * property to be inserted, has to have at least one step
209 * @throws PropertyPathResolvingException
210 * if the path violates the feature type's schema
211 */
212 public void addSortProperty( PropertyPath property )
213 throws PropertyPathResolvingException {
214 MappedPropertyType pt = validate( property, false );
215 if ( pt instanceof MappedSimplePropertyType ) {
216 SimpleContent content = ( (MappedSimplePropertyType) pt ).getContent();
217 if ( content.isSortable() ) {
218 insertValidatedPath( property );
219 } else {
220 String msg = "Skipping property '" + property + "' as sort criterion.";
221 LOG.logDebug( msg );
222 // add SimplePropertyNode to root node (because table path is irrelevant)
223 String[] tableAliases = generateTableAliases( pt );
224 PropertyNode propertyNode = new SimplePropertyNode( (MappedSimplePropertyType) pt, roots[0],
225 tableAliases );
226 this.propertyPaths.add( property );
227 this.propertyNodes.add( propertyNode );
228 // root.addPropertyNode( propertyNode );
229 }
230 } else {
231 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_SORT1", property );
232 throw new PropertyPathResolvingException( msg );
233 }
234 }
235
236 /**
237 * Validates the (normalized) {@link PropertyPath} against the {@link MappedGMLSchema} and returns the
238 * {@link MappedPropertyType} that the path refers to.
239 *
240 * @param propertyPath
241 * PropertyPath to be validated, has to have at least one step
242 * @param forceUniquePath
243 * if set to true, an exeption is thrown if the path is not unique, i.e. at least one property on the
244 * path has maxOccurs set to a value > 1
245 * @return the property type that the path ends with
246 * @throws PropertyPathResolvingException
247 * if the path violates the feature type's schema
248 */
249 private MappedPropertyType validate( PropertyPath propertyPath, boolean forceUniquePath )
250 throws PropertyPathResolvingException {
251
252 MappedPropertyType endingPt = null;
253 MappedFeatureType currentFt = null;
254 int firstPropertyPos = 1;
255
256 // check if path starts with (valid) alias
257 QualifiedName firstStep = propertyPath.getStep( 0 ).getPropertyName();
258 if ( firstStep.getLocalName().startsWith( "$" ) ) {
259 // path starts with alias
260 String ftAlias = firstStep.getLocalName().substring( 1 );
261 FeatureTypeNode rootNode = this.ftAliasToRootFtNode.get( ftAlias );
262 if ( rootNode == null ) {
263 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE6", propertyPath,
264 firstStep.getLocalName() );
265 throw new PropertyPathResolvingException( msg );
266 }
267 currentFt = rootNode.getFeatureType();
268 } else {
269 // path does not start with an alias
270 if ( this.roots.length > 1 ) {
271 LOG.logDebug( "Multiple (join) feature type request. First step of '" + propertyPath
272 + "' must be the name (or an alias) of a requested feature type then..." );
273 QualifiedName ftName = propertyPath.getStep( 0 ).getPropertyName();
274 for ( FeatureTypeNode rootNode : this.roots ) {
275 if ( rootNode.getFeatureType().getName().equals( ftName ) ) {
276 currentFt = rootNode.getFeatureType();
277 break;
278 }
279 }
280 if ( currentFt == null ) {
281 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE5", propertyPath,
282 propertyPath.getStep( 0 ) );
283 throw new PropertyPathResolvingException( msg );
284 }
285 } else {
286 currentFt = this.roots[0].getFeatureType();
287 LOG.logDebug( "Single feature type request. Trying to validate '" + propertyPath
288 + "' against schema of feature type '" + currentFt.getName() + "'..." );
289
290 QualifiedName elementName = propertyPath.getStep( 0 ).getPropertyName();
291
292 // must be the name of the feature type or the name of a property of the feature
293 // type
294 if ( elementName.equals( currentFt.getName() ) ) {
295 LOG.logDebug( "First step matches the name of the feature type." );
296 } else {
297 LOG.logDebug( "First step does not match the name of the feature type. "
298 + "Must be the name of a property then." );
299 firstPropertyPos = 0;
300 }
301 }
302 }
303
304 for ( int step = firstPropertyPos; step < propertyPath.getSteps(); step += 2 ) {
305 LOG.logDebug( "Looking up property: " + propertyPath.getStep( step ).getPropertyName() );
306 endingPt = getPropertyType( currentFt, propertyPath.getStep( step ) );
307
308 if ( endingPt == null ) {
309 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE4", propertyPath, step,
310 propertyPath.getStep( step ), currentFt.getName(),
311 propertyPath.getStep( step ) );
312 throw new PropertyPathResolvingException( msg );
313 }
314
315 if ( forceUniquePath ) {
316 if ( endingPt.getMaxOccurs() != 1 ) {
317 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_SORT2", propertyPath, step,
318 endingPt.getName() );
319 throw new PropertyPathResolvingException( msg );
320 }
321 }
322
323 if ( endingPt instanceof MappedSimplePropertyType ) {
324 if ( step < propertyPath.getSteps() - 1 ) {
325 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE1", propertyPath, step,
326 endingPt.getName(), "simple" );
327 throw new PropertyPathResolvingException( msg );
328 }
329 } else if ( endingPt instanceof MappedGeometryPropertyType ) {
330 if ( step < propertyPath.getSteps() - 1 ) {
331 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE1", propertyPath, step,
332 endingPt.getName(), "geometry" );
333 throw new PropertyPathResolvingException( msg );
334 }
335 } else if ( endingPt instanceof MappedFeaturePropertyType ) {
336 if ( step == propertyPath.getSteps() - 1 ) {
337 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE2", propertyPath, step,
338 endingPt.getName() );
339 throw new PropertyPathResolvingException( msg );
340 }
341 MappedFeaturePropertyType pt = (MappedFeaturePropertyType) endingPt;
342 MappedFeatureType[] allowedTypes = pt.getFeatureTypeReference().getFeatureType().getConcreteSubstitutions();
343 QualifiedName givenTypeName = propertyPath.getStep( step + 1 ).getPropertyName();
344
345 // check if an alias is used
346 if ( givenTypeName.getLocalName().startsWith( "$" ) ) {
347 String ftAlias = givenTypeName.getLocalName().substring( 1 );
348 FeatureTypeNode ftNode = this.ftAliasToRootFtNode.get( ftAlias );
349 if ( ftNode == null ) {
350 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE6", propertyPath, step + 1,
351 propertyPath.getStep( step + 1 ) );
352 throw new PropertyPathResolvingException( msg );
353 }
354 givenTypeName = ftNode.getFeatureType().getName();
355 }
356
357 MappedFeatureType givenType = null;
358
359 for ( int i = 0; i < allowedTypes.length; i++ ) {
360 if ( allowedTypes[i].getName().equals( givenTypeName ) ) {
361 givenType = allowedTypes[i];
362 break;
363 }
364 }
365 if ( givenType == null ) {
366 StringBuffer validTypeList = new StringBuffer();
367 for ( int i = 0; i < allowedTypes.length; i++ ) {
368 validTypeList.append( '\'' );
369 validTypeList.append( allowedTypes[i].getName() );
370 validTypeList.append( '\'' );
371 if ( i != allowedTypes.length - 1 ) {
372 validTypeList.append( ", " );
373 }
374 }
375 String msg = Messages.getMessage( "DATASTORE_PROPERTY_PATH_RESOLVE3", propertyPath, step + 1,
376 propertyPath.getStep( step + 1 ), validTypeList );
377 throw new PropertyPathResolvingException( msg.toString() );
378 }
379 currentFt = pt.getFeatureTypeReference().getFeatureType();
380 } else {
381 assert ( false );
382 }
383 }
384 return endingPt;
385 }
386
387 /**
388 * Inserts the (already validated!!!) {@link PropertyPath} into the query tree.
389 *
390 * @see #validate(PropertyPath, boolean)
391 *
392 * @param propertyPath
393 * validated PropertyPath to be inserted into the tree
394 */
395 private void insertValidatedPath( PropertyPath propertyPath ) {
396
397 LOG.logDebug( "Inserting '" + propertyPath + "' into the query table tree." );
398
399 FeatureTypeNode ftNode = null;
400 int firstPropertyPos = 1;
401
402 // check if path starts with an alias
403 QualifiedName firstStep = propertyPath.getStep( 0 ).getPropertyName();
404 if ( firstStep.getLocalName().startsWith( "$" ) ) {
405 String ftAlias = firstStep.getLocalName().substring( 1 );
406 ftNode = this.ftAliasToRootFtNode.get( ftAlias );
407 } else {
408 if ( this.roots.length > 1 ) {
409 QualifiedName ftName = propertyPath.getStep( 0 ).getPropertyName();
410 for ( FeatureTypeNode rootNode : this.roots ) {
411 if ( rootNode.getFeatureType().getName().equals( ftName ) ) {
412 ftNode = rootNode;
413 break;
414 }
415 }
416 } else {
417 PropertyPathStep step = propertyPath.getStep( 0 );
418 QualifiedName elementName = step.getPropertyName();
419
420 // must be the name of the feature type or the name of a property of the feature type
421 if ( elementName.equals( this.roots[0].getFeatureType().getName() ) ) {
422 LOG.logDebug( "First step matches the name of the feature type." );
423 } else {
424 LOG.logDebug( "First step does not match the name of the feature type. "
425 + "Must be the name of a property then." );
426 firstPropertyPos = 0;
427 }
428 ftNode = this.roots[0];
429 }
430 }
431
432 PropertyNode propNode = null;
433 for ( int i = firstPropertyPos; i < propertyPath.getSteps(); i += 2 ) {
434
435 // check for property with step name in the feature type
436 MappedFeatureType currentFt = ftNode.getFeatureType();
437 MappedPropertyType pt = getPropertyType( currentFt, propertyPath.getStep( i ) );
438
439 // check if feature type node already has such a property node, add it otherwise
440 propNode = ftNode.getPropertyNode( pt );
441 if ( propNode == null || propNode.getProperty().getMaxOccurs() != 1 ) {
442 propNode = createPropertyNode( ftNode, pt );
443 ftNode.addPropertyNode( propNode );
444 }
445
446 if ( i + 1 < propertyPath.getSteps() ) {
447 // more steps? propNode must be a FeaturePropertyNode then
448 assert propNode instanceof FeaturePropertyNode;
449 QualifiedName featureStep = propertyPath.getStep( i + 1 ).getPropertyName();
450 ftNode = findOrAddSubFtNode( (FeaturePropertyNode) propNode, featureStep );
451 }
452 }
453
454 this.propertyPaths.add( propertyPath );
455 this.propertyNodes.add( propNode );
456
457 // // "equal" path is already registered, map this one to existing instance
458 // if ( getPropertyNode( propertyPath ) == null ) {
459 // this.propertyPaths.add( propertyPath );
460 // this.propertyNodes.add( propNode );
461 // }
462 }
463
464 private PropertyNode createPropertyNode( FeatureTypeNode ftNode, MappedPropertyType pt ) {
465
466 PropertyNode propNode = null;
467 if ( pt instanceof MappedSimplePropertyType ) {
468 String[] tableAliases = generateTableAliases( pt );
469 propNode = new SimplePropertyNode( (MappedSimplePropertyType) pt, ftNode, tableAliases );
470 } else if ( pt instanceof MappedGeometryPropertyType ) {
471 String[] tableAliases = generateTableAliases( pt );
472 propNode = new GeometryPropertyNode( (MappedGeometryPropertyType) pt, ftNode, tableAliases );
473 } else if ( pt instanceof MappedFeaturePropertyType ) {
474 String[] tableAliases = this.aliasGenerator.generateUniqueAliases( pt.getTableRelations().length - 1 );
475 propNode = new FeaturePropertyNode( ( (MappedFeaturePropertyType) pt ), ftNode, tableAliases );
476 } else {
477 assert ( false );
478 }
479 return propNode;
480 }
481
482 /**
483 * Returns a {@link FeatureTypeNode} that select a child of the given {@link FeaturePropertyNode}.
484 * <p>
485 * If the step specifies a feature type alias (instead of the feature type name), the corresponding root feature
486 * type node is returned.
487 *
488 * @param propNode
489 * @param featureStep
490 */
491 private FeatureTypeNode findOrAddSubFtNode( FeaturePropertyNode propNode, QualifiedName featureStep ) {
492
493 FeatureTypeNode childNode = null;
494
495 // check if step specifies an alias -> use corresponding root feature node then
496 if ( featureStep.getLocalName().startsWith( "$" ) ) {
497 String alias = featureStep.getLocalName().substring( 1 );
498 FeatureTypeNode [] childNodes = propNode.getFeatureTypeNodes();
499 for ( FeatureTypeNode node : childNodes ) {
500 if (alias.equals( node.getFtAlias() )) {
501 childNode = node;
502 break;
503 }
504 }
505 if (childNode == null) {
506 childNode = this.ftAliasToRootFtNode.get( alias );
507 propNode.addFeatureTypeNode( childNode );
508 }
509 } else {
510 FeatureTypeNode [] subFtNodes = propNode.getFeatureTypeNodes();
511 for ( FeatureTypeNode node : subFtNodes ) {
512 if (node.getFeatureType().getName().equals( featureStep )) {
513 childNode = node;
514 break;
515 }
516 }
517 if (childNode == null) {
518 MappedFeatureType subFt = this.roots[0].getFeatureType().getGMLSchema().getFeatureType( featureStep );
519 String tableAlias = this.aliasGenerator.generateUniqueAlias();
520 childNode = new FeatureTypeNode( subFt, null, tableAlias );
521 propNode.addFeatureTypeNode( childNode );
522 }
523 }
524
525 return childNode;
526 }
527
528 // private FeaturePropertyNode createFeaturePropertyNode( FeatureTypeNode ftNode, MappedFeaturePropertyType pt) {
529 //
530 // // MappedFeatureType[] allowedTypes = pt.getFeatureTypeReference().getFeatureType().getConcreteSubstitutions();
531 // // QualifiedName givenTypeName = propertyPath.getStep( step + 1 ).getPropertyName();
532 // // MappedFeatureType givenType = null;
533 //
534 //
535 // }
536
537 // private void addPathFragment( FeatureTypeNode ftNode, PropertyPath propertyPath, int startStep ) {
538 //
539 // for ( int step = startStep; step < propertyPath.getSteps(); step += 2 ) {
540 // LOG.logDebug( "Looking up property: " + propertyPath.getStep( step ).getPropertyName() );
541 // MappedPropertyType pt = getPropertyType( ftNode.getFeatureType(), propertyPath.getStep( step ) );
542 // if ( pt instanceof MappedSimplePropertyType ) {
543 // addSimplePropertyNode( ftNode, (MappedSimplePropertyType) pt, propertyPath, step );
544 // break;
545 // } else if ( pt instanceof MappedGeometryPropertyType ) {
546 // addGeometryPropertyNode( ftNode, (MappedGeometryPropertyType) pt, propertyPath, step );
547 // break;
548 // } else if ( pt instanceof MappedFeaturePropertyType ) {
549 // MappedFeaturePropertyType featurePT = (MappedFeaturePropertyType) pt;
550 // ftNode = addFeaturePropertyNode( ftNode, featurePT, propertyPath, step );
551 // } else {
552 // assert ( false );
553 // }
554 // }
555 // }
556
557 /**
558 * Returns the {@link MappedPropertyType} for the given {@link MappedFeatureType} that matches the given
559 * {@link PropertyPathStep}.
560 *
561 * @param ft
562 * @param step
563 * @return matching property type or null, if none exists
564 */
565 private MappedPropertyType getPropertyType( MappedFeatureType ft, PropertyPathStep step ) {
566
567 MappedPropertyType pt = null;
568 QualifiedName name = step.getPropertyName();
569
570 if ( step instanceof AttributeStep ) {
571 // TODO remove handling of gml:id here (after adaptation of feature model)
572 if ( CommonNamespaces.GMLNS.equals( name.getNamespace() ) && "id".equals( name.getLocalName() ) ) {
573 MappedGMLId gmlId = ft.getGMLId();
574 pt = new MappedSimplePropertyType( name, Types.VARCHAR, 1, 1, false, null, gmlId.getIdFields()[0] );
575 }
576 } else {
577 // normal property (not gml:id)
578 pt = (MappedPropertyType) ft.getProperty( name );
579 }
580
581 return pt;
582 }
583
584 // private void addSimplePropertyNode( FeatureTypeNode featureTypeNode, MappedSimplePropertyType propertyType,
585 // PropertyPath propertyPath, int step ) {
586 //
587 // assert ( step == propertyPath.getSteps() - 1 );
588 // String[] tableAliases = generateTableAliases( propertyType );
589 // PropertyNode propertyNode = new SimplePropertyNode( propertyType, featureTypeNode, tableAliases );
590 // this.propertyPaths.add( propertyPath );
591 // this.propertyNodes.add( propertyNode );
592 // featureTypeNode.addPropertyNode( propertyNode );
593 // }
594 //
595 // private void addGeometryPropertyNode( FeatureTypeNode featureTypeNode, MappedGeometryPropertyType propertyType,
596 // PropertyPath propertyPath, int step ) {
597 //
598 // assert ( step == propertyPath.getSteps() - 1 );
599 // String[] tableAliases = generateTableAliases( propertyType );
600 // PropertyNode propertyNode = new GeometryPropertyNode( propertyType, featureTypeNode, tableAliases );
601 // this.propertyPaths.add( propertyPath );
602 // this.propertyNodes.add( propertyNode );
603 // featureTypeNode.addPropertyNode( propertyNode );
604 // }
605 //
606 // private FeatureTypeNode addFeaturePropertyNode( FeatureTypeNode ftNode, MappedFeaturePropertyType pt,
607 // PropertyPath propertyPath, int step ) {
608 //
609 // assert ( step < propertyPath.getSteps() - 1 );
610 // MappedFeatureType[] allowedTypes = pt.getFeatureTypeReference().getFeatureType().getConcreteSubstitutions();
611 // QualifiedName givenTypeName = propertyPath.getStep( step + 1 ).getPropertyName();
612 // MappedFeatureType givenType = null;
613 //
614 // for ( int i = 0; i < allowedTypes.length; i++ ) {
615 // if ( allowedTypes[i].getName().equals( givenTypeName ) ) {
616 // givenType = allowedTypes[i];
617 // break;
618 // }
619 // }
620 // assert ( givenType != null );
621 //
622 // // TODO make proper
623 // String[] tableAliases = this.aliasGenerator.generateUniqueAliases( pt.getTableRelations().length - 1 );
624 // String tableAlias = this.aliasGenerator.generateUniqueAlias();
625 // FeatureTypeNode childFeatureTypeNode = new FeatureTypeNode( givenType, null, tableAlias );
626 //
627 // FeatureType ft = pt.getFeatureTypeReference().getFeatureType();
628 // LOG.logDebug( "featureType: " + ft.getName() );
629 //
630 // PropertyNode propertyNode = new FeaturePropertyNode( pt, ftNode, tableAliases, childFeatureTypeNode );
631 // // this.propertyPaths.add (propertyPath);
632 // // this.propertyNodes.add (propertyNode);
633 // ftNode.addPropertyNode( propertyNode );
634 // return childFeatureTypeNode;
635 // }
636
637 private String[] generateTableAliases( MappedPropertyType pt ) {
638 String[] aliases = null;
639 TableRelation[] relations = pt.getTableRelations();
640 if ( relations != null ) {
641 aliases = new String[relations.length];
642 for ( int i = 0; i < aliases.length; i++ ) {
643 aliases[i] = this.aliasGenerator.generateUniqueAlias();
644 }
645 }
646 return aliases;
647 }
648
649 @Override
650 public String toString() {
651 StringBuffer sb = new StringBuffer();
652 for ( FeatureTypeNode root : this.roots ) {
653 sb.append( root.toString( "" ) );
654 sb.append( '\n' );
655 }
656 return sb.toString();
657 }
658 }