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