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    }