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