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 }