    001    //$HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
    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
    037    package org.deegree.io.datastore.sql;
    038
    039    import java.util.ArrayList;
    040    import java.util.Arrays;
    041    import java.util.Collection;
    042    import java.util.HashMap;
    043    import java.util.List;
    044    import java.util.Map;
    045
    046    import org.deegree.framework.log.ILogger;
    047    import org.deegree.framework.log.LoggerFactory;
    048    import org.deegree.io.datastore.DatastoreException;
    049    import org.deegree.io.datastore.PropertyPathResolver;
    050    import org.deegree.io.datastore.PropertyPathResolvingException;
    051    import org.deegree.io.datastore.schema.MappedFeatureType;
    052    import org.deegree.io.datastore.schema.MappedPropertyType;
    053    import org.deegree.io.datastore.schema.content.SimpleContent;
    054    import org.deegree.model.feature.schema.GeometryPropertyType;
    055    import org.deegree.ogcbase.PropertyPath;
    056    import org.deegree.ogcbase.PropertyPathFactory;
    057    import org.deegree.ogcbase.PropertyPathStep;
    058    import org.deegree.ogcwebservices.wfs.operation.Query;
    059
    060    /**
    061     * Responsible for managing the mapping of properties to table columns SQL and their position in the SQL result set.
    062     *
    063     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
    064     * @author last edited by: $Author:$
    065     *
    066     * @version $Revision:$, $Date:$
    067     */
    068    class SelectManager {
    069
    070        private static final ILogger LOG = LoggerFactory.getLogger( SelectManager.class );
    071
    072        private MappedFeatureType[] fts;
    073
    074        private Map<MappedPropertyType, Collection<PropertyPath>>[] allFetchProps;
    075
    076        private List<List<SimpleContent>>[] allFetchContents;
    077
    078        private Map<SimpleContent, Integer>[] allResultPosMaps;
    079
    080        private int fetchContentsCount;
    081
    082        // requested properties, must contain exactly one list for each targeted feature type
    083        private List<PropertyPath>[] selectedProps;
    084
    085        private String[] ftAliases;
    086
    087        // TODO remove this
    088        private FeatureFetcher fetcher;
    089
    090        List<PropertyPath> augmentedGeometryProps = new ArrayList<PropertyPath>();
    091
    092        SelectManager( Query query, MappedFeatureType[] rootFts, FeatureFetcher fetcher ) throws DatastoreException {
    093
    094            this.fts = rootFts;
    095            this.ftAliases = query.getAliases();
    096            this.selectedProps = PropertyPathResolver.normalizePropertyPaths( this.fts, this.ftAliases,
    097                                                                              query.getPropertyNames() );
    098
    099            // hack that ensures that all geometry properties are fetched for correct boundedBy information
    100            if ( fts.length == 1 ) {
    101                PropertyPathStep ftStep = PropertyPathFactory.createPropertyPathStep( fts[0].getName() );
    102                PropertyPath fullFeature = PropertyPathFactory.createPropertyPath( fts[0].getName() );
    103                for ( GeometryPropertyType geoProp : fts[0].getGeometryProperties() ) {
    104                    PropertyPathStep[] steps = new PropertyPathStep[] {
    105                                                                       ftStep,
    106                                                                       PropertyPathFactory.createPropertyPathStep( geoProp.getName() ) };
    107                    PropertyPath geoPropPath = PropertyPathFactory.createPropertyPath( Arrays.asList( steps ) );
    108                    boolean found = geoProp.getMinOccurs() > 0;
    109                    if ( !found ) {
    110                        for ( PropertyPath selectedPath : selectedProps[0] ) {
    111                            if ( selectedPath.equals( fullFeature ) || selectedPath.equals( geoPropPath ) ) {
    112                                found = true;
    113                                break;
    114                            }
    115                        }
    116                    }
    117                    if ( !found ) {
    118                        LOG.logDebug( "Augmenting geometry property '" + geoPropPath
    119                                      + "' to ensure that all geometry properties are selected (boundedBy-hack)." );
    120                        augmentedGeometryProps.add( geoPropPath );
    121                        selectedProps[0].add( geoPropPath );
    122                    }
    123                }
    124            }
    125
    126            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    127                LOG.logDebug( "Selected properties (normalized): " );
    128                for ( int i = 0; i < fts.length; i++ ) {
    129                    List<PropertyPath> props = this.selectedProps[i];
    130                    LOG.logDebug( "Selected properties (normalized) for feature type '" + fts[i].getName() + "'." );
    131                    for ( PropertyPath path : props ) {
    132                        LOG.logDebug( " - " + path );
    133                    }
    134                }
    135            }
    136
    137            this.allFetchProps = new Map[this.fts.length];
    138            this.allFetchContents = new List[this.fts.length];
    139            this.allResultPosMaps = new Map[this.fts.length];
    140            this.fetcher = fetcher;
    141
    142            determineInitialFetchProperties();
    143            determineFetchContents();
    144            buildResultPosMaps();
    145        }
    146
    147        private void determineInitialFetchProperties()
    148                                throws PropertyPathResolvingException {
    149
    150            LOG.logDebug( "Determining fetch properties for all requested feature types." );
    151
    152            for ( int i = 0; i < this.fts.length; i++ ) {
    153                MappedFeatureType rootFt = this.fts[i];
    154                LOG.logDebug( "Feature type: " + rootFt.getName() + " (alias: "
    155                              + ( this.ftAliases != null ? this.ftAliases[i] : "-" ) + ")" );
    156                PropertyPath[] requestedProps = this.selectedProps[i].toArray( new PropertyPath[this.selectedProps[i].size()] );
    157                for ( PropertyPath path : requestedProps ) {
    158                    LOG.logDebug( "Requested property: " + path );
    159                }
    160                Map<MappedPropertyType, Collection<PropertyPath>> ftRequestedProps = PropertyPathResolver.determineFetchProperties(
    161                                                                                                                                    rootFt,
    162                                                                                                                                    this.ftAliases != null ? this.ftAliases[i]
    163                                                                                                                                                          : null,
    164                                                                                                                                    requestedProps );
    165                LOG.logDebug( "All properties needed for feature type: " + rootFt.getName() );
    166                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    167                    for ( MappedPropertyType pt : ftRequestedProps.keySet() ) {
    168                        LOG.logDebug( "-" + pt.getName() );
    169                    }
    170                }
    171                this.allFetchProps[i] = ftRequestedProps;
    172            }
    173        }
    174
    175        private void determineFetchContents()
    176                                throws DatastoreException {
    177            LOG.logDebug( "Determining initial fetch contents for all requested feature types..." );
    178
    179            for ( int i = 0; i < this.fts.length; i++ ) {
    180                MappedFeatureType rootFt = this.fts[i];
    181                MappedPropertyType[] requestedProps = new MappedPropertyType[this.allFetchProps[i].size()];
    182                requestedProps = this.allFetchProps[i].keySet().toArray( requestedProps );
    183                List<List<SimpleContent>> ftFetchContents = null;
    184                if ( requestedProps.length > 0 ) {
    185                    ftFetchContents = fetcher.determineFetchContents( rootFt, requestedProps );
    186                } else {
    187                    ftFetchContents = new ArrayList<List<SimpleContent>>();
    188                }
    189                this.allFetchContents[i] = ftFetchContents;
    190
    191                LOG.logDebug( "Will fetch the following columns initially (for feature type " + rootFt.getName() + "):" );
    192                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    193                    for ( List<SimpleContent> list : ftFetchContents ) {
    194                        SimpleContent representer = list.get( 0 );
    195                        LOG.logDebug( "-" + representer );
    196                    }
    197                }
    198                this.fetchContentsCount += ftFetchContents.size();
    199            }
    200        }
    201
    202        private void buildResultPosMaps() {
    203
    204            int currentRSPos = 0;
    205            for ( int i = 0; i < this.allFetchContents.length; i++ ) {
    206                List<List<SimpleContent>> ftFetchContents = this.allFetchContents[i];
    207                Map<SimpleContent, Integer> ftResultPosMap = new HashMap<SimpleContent, Integer>();
    208                for ( int j = 0; j < ftFetchContents.size(); j++ ) {
    209                    for ( SimpleContent content : ftFetchContents.get( j ) ) {
    210                        ftResultPosMap.put( content, j + currentRSPos );
    211                    }
    212                }
    213                this.allResultPosMaps[i] = ftResultPosMap;
    214                currentRSPos += ftFetchContents.size();
    215            }
    216        }
    217
    218        List<List<SimpleContent>>[] getAllFetchContents() {
    219            return this.allFetchContents;
    220        }
    221
    222        Map<SimpleContent, Integer>[] getResultPosMaps() {
    223            return this.allResultPosMaps;
    224        }
    225
    226        Map<MappedPropertyType, Collection<PropertyPath>>[] getAllFetchProps() {
    227            return this.allFetchProps;
    228        }
    229
    230        int getFetchContentCount() {
    231            return this.fetchContentsCount;
    232        }
    233
    234        private int getActualFeatureTupleLength() {
    235            int i = 0;
    236            for ( List<List<SimpleContent>> ftFetchContents : this.allFetchContents ) {
    237                if ( ftFetchContents.size() > 0 ) {
    238                    i++;
    239                }
    240            }
    241            return i;
    242        }
    243
    244        int[] getIncludedFtIdx() {
    245            int[] includedFtIdx = new int[getActualFeatureTupleLength()];
    246            int i = 0;
    247            int idx = 0;
    248            for ( List<List<SimpleContent>> ftFetchContents : this.allFetchContents ) {
    249                if ( ftFetchContents.size() > 0 ) {
    250                    includedFtIdx[idx] = i;
    251                    idx++;
    252                }
    253                i++;
    254            }
    255            return includedFtIdx;
    256        }
    257
    258        @Override
    259        public String toString() {
    260            StringBuffer sb = new StringBuffer();
    261            int rsOffset = 0;
    262
    263            for ( int i = 0; i < this.fts.length; i++ ) {
    264                sb.append( "Properties needed for feature type '" + this.fts[i].getName() + "':\n" );
    265                Map<MappedPropertyType, Collection<PropertyPath>> ftFetchProps = this.allFetchProps[i];
    266                for ( MappedPropertyType pt : ftFetchProps.keySet() ) {
    267                    sb.append( " - " );
    268                    sb.append( pt.getName().getLocalName() );
    269                    sb.append( ", requesting PropertyNames: " );
    270                    Collection<PropertyPath> requestingPaths = ftFetchProps.get( pt );
    271                    for ( PropertyPath path : requestingPaths ) {
    272                        sb.append( path );
    273                        sb.append( " " );
    274                    }
    275                    sb.append( '\n' );
    276                }
    277
    278                sb.append( "Fields to be fetched for feature type '" + this.fts[i].getName() + "':\n" );
    279                List<List<SimpleContent>> ftFetchContents = this.allFetchContents[i];
    280                for ( int j = 0; j < ftFetchContents.size(); j++ ) {
    281                    List<SimpleContent> sameField = ftFetchContents.get( j );
    282                    sb.append( " - ResultSet[" );
    283                    sb.append( j + rsOffset );
    284                    sb.append( "], SimpleContent: " );
    285                    sb.append( '\'' );
    286                    sb.append( sameField.get( 0 ) );
    287                    sb.append( '\'' );
    288                    sb.append( '\n' );
    289                }
    290                rsOffset += ftFetchContents.size();
    291            }
    292            return sb.toString();
    293        }
    294    }