001    //$HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    
039    package org.deegree.io.datastore.sql;
040    
041    import java.util.ArrayList;
042    import java.util.Collection;
043    import java.util.HashMap;
044    import java.util.List;
045    import java.util.Map;
046    
047    import org.deegree.framework.log.ILogger;
048    import org.deegree.framework.log.LoggerFactory;
049    import org.deegree.io.datastore.DatastoreException;
050    import org.deegree.io.datastore.PropertyPathResolver;
051    import org.deegree.io.datastore.PropertyPathResolvingException;
052    import org.deegree.io.datastore.schema.MappedFeatureType;
053    import org.deegree.io.datastore.schema.MappedPropertyType;
054    import org.deegree.io.datastore.schema.content.SimpleContent;
055    import org.deegree.ogcbase.PropertyPath;
056    import org.deegree.ogcwebservices.wfs.operation.Query;
057    
058    /**
059     * Responsible for managing the mapping of properties to table columns SQL and their position in the SQL result set.
060     * 
061     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
062     * @author last edited by: $Author:$
063     * 
064     * @version $Revision:$, $Date:$
065     */
066    class SelectManager {
067    
068        private static final ILogger LOG = LoggerFactory.getLogger( SelectManager.class );
069    
070        private MappedFeatureType[] fts;
071    
072        private Map<MappedPropertyType, Collection<PropertyPath>>[] allFetchProps;
073    
074        private List<List<SimpleContent>>[] allFetchContents;
075    
076        private Map<SimpleContent, Integer>[] allResultPosMaps;
077    
078        private int fetchContentsCount;
079    
080        // requested properties, must contain exactly one list for each targeted feature type
081        private List<PropertyPath>[] selectedProps;
082    
083        private String[] ftAliases;
084    
085        // TODO remove this
086        private FeatureFetcher fetcher;
087    
088        @SuppressWarnings("unchecked")
089        SelectManager( Query query, MappedFeatureType[] rootFts, FeatureFetcher fetcher ) throws DatastoreException {
090    
091            this.fts = rootFts;
092            this.ftAliases = query.getAliases();
093            this.selectedProps = PropertyPathResolver.normalizePropertyPaths( this.fts, this.ftAliases,
094                                                                              query.getPropertyNames() );
095    
096            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
097                LOG.logDebug( "Selected properties (normalized): " );
098                for ( int i = 0; i < fts.length; i++ ) {
099                    List<PropertyPath> props = this.selectedProps[i];
100                    LOG.logDebug( "Selected properties (normalized) for feature type '" + fts[i].getName() + "'." );
101                    for ( PropertyPath path : props ) {
102                        LOG.logDebug( " - " + path );
103                    }
104                }
105            }
106    
107            this.allFetchProps = new Map[this.fts.length];
108            this.allFetchContents = new List[this.fts.length];
109            this.allResultPosMaps = new Map[this.fts.length];
110            this.fetcher = fetcher;
111    
112            determineInitialFetchProperties();
113            determineFetchContents();
114            buildResultPosMaps();
115        }
116    
117        private void determineInitialFetchProperties()
118                                throws PropertyPathResolvingException {
119    
120            LOG.logDebug( "Determining fetch properties for all requested feature types." );
121    
122            for ( int i = 0; i < this.fts.length; i++ ) {
123                MappedFeatureType rootFt = this.fts[i];
124                LOG.logDebug( "Feature type: " + rootFt.getName() + " (alias: "
125                              + ( this.ftAliases != null ? this.ftAliases[i] : "-" ) + ")" );
126                PropertyPath[] requestedProps = this.selectedProps[i].toArray( new PropertyPath[this.selectedProps[i].size()] );
127                for ( PropertyPath path : requestedProps ) {
128                    LOG.logDebug( "Requested property: " + path );
129                }
130                Map<MappedPropertyType, Collection<PropertyPath>> ftRequestedProps = PropertyPathResolver.determineFetchProperties(
131                                                                                                                                    rootFt,
132                                                                                                                                    this.ftAliases != null ? this.ftAliases[i]
133                                                                                                                                                          : null,
134                                                                                                                                    requestedProps );
135                LOG.logDebug( "All properties needed for feature type: " + rootFt.getName() );
136                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
137                    for ( MappedPropertyType pt : ftRequestedProps.keySet() ) {
138                        LOG.logDebug( "-" + pt.getName() );
139                    }
140                }
141                this.allFetchProps[i] = ftRequestedProps;
142            }
143        }
144    
145        private void determineFetchContents()
146                                throws DatastoreException {
147            LOG.logDebug( "Determining initial fetch contents for all requested feature types..." );
148    
149            for ( int i = 0; i < this.fts.length; i++ ) {
150                MappedFeatureType rootFt = this.fts[i];
151                MappedPropertyType[] requestedProps = new MappedPropertyType[this.allFetchProps[i].size()];
152                requestedProps = this.allFetchProps[i].keySet().toArray( requestedProps );
153                List<List<SimpleContent>> ftFetchContents = null;
154                if (requestedProps.length > 0) {
155                    ftFetchContents = fetcher.determineFetchContents( rootFt, requestedProps );
156                } else {
157                    ftFetchContents = new ArrayList<List<SimpleContent>> ();
158                }
159                this.allFetchContents[i] = ftFetchContents;
160    
161                LOG.logDebug( "Will fetch the following columns initially (for feature type " + rootFt.getName() + "):" );
162                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
163                    for ( List<SimpleContent> list : ftFetchContents ) {
164                        SimpleContent representer = list.get( 0 );
165                        LOG.logDebug( "-" + representer );
166                    }
167                }
168                this.fetchContentsCount += ftFetchContents.size();
169            }
170        }
171    
172        private void buildResultPosMaps() {
173    
174            int currentRSPos = 0;
175            for ( int i = 0; i < this.allFetchContents.length; i++ ) {
176                List<List<SimpleContent>> ftFetchContents = this.allFetchContents[i];
177                Map<SimpleContent, Integer> ftResultPosMap = new HashMap<SimpleContent, Integer>();
178                for ( int j = 0; j < ftFetchContents.size(); j++ ) {
179                    for ( SimpleContent content : ftFetchContents.get( j ) ) {
180                        ftResultPosMap.put( content, j + currentRSPos );
181                    }
182                }
183                this.allResultPosMaps[i] = ftResultPosMap;
184                currentRSPos += ftFetchContents.size();
185            }
186        }
187    
188        List<List<SimpleContent>>[] getAllFetchContents() {
189            return this.allFetchContents;
190        }
191    
192        Map<SimpleContent, Integer>[] getResultPosMaps() {
193            return this.allResultPosMaps;
194        }
195    
196        Map<MappedPropertyType, Collection<PropertyPath>>[] getAllFetchProps() {
197            return this.allFetchProps;
198        }
199    
200        int getFetchContentCount() {
201            return this.fetchContentsCount;
202        }
203        
204        private int getActualFeatureTupleLength () {
205            int i = 0; 
206            for (List<List<SimpleContent>> ftFetchContents: this.allFetchContents) {
207                if (ftFetchContents.size() > 0) {
208                    i++;
209                }
210            }
211            return i;
212        }
213        
214        int []  getIncludedFtIdx () {
215            int [] includedFtIdx = new int [getActualFeatureTupleLength()];
216            int i = 0;
217            int idx = 0;
218            for (List<List<SimpleContent>> ftFetchContents: this.allFetchContents) {
219                if (ftFetchContents.size() > 0) {
220                    includedFtIdx [idx] = i;
221                    idx++;
222                }
223                i++;
224            }
225            return includedFtIdx;
226        }   
227    
228        @Override
229        public String toString() {
230            StringBuffer sb = new StringBuffer();
231            int rsOffset = 0;
232    
233            for ( int i = 0; i < this.fts.length; i++ ) {
234                sb.append( "Properties needed for feature type '" + this.fts[i].getName() + "':\n" );
235                Map<MappedPropertyType, Collection<PropertyPath>> ftFetchProps = this.allFetchProps[i];
236                for ( MappedPropertyType pt : ftFetchProps.keySet() ) {
237                    sb.append( " - " );
238                    sb.append( pt.getName().getLocalName() );
239                    sb.append( ", requesting PropertyNames: " );
240                    Collection<PropertyPath> requestingPaths = ftFetchProps.get( pt );
241                    for ( PropertyPath path : requestingPaths ) {
242                        sb.append( path );
243                        sb.append( " " );
244                    }
245                    sb.append( '\n' );
246                }
247    
248                sb.append( "Fields to be fetched for feature type '" + this.fts[i].getName() + "':\n" );
249                List<List<SimpleContent>> ftFetchContents = this.allFetchContents[i];
250                for ( int j = 0; j < ftFetchContents.size(); j++ ) {
251                    List<SimpleContent> sameField = ftFetchContents.get( j );
252                    sb.append( " - ResultSet[" );
253                    sb.append( j + rsOffset );
254                    sb.append( "], SimpleContent: " );
255                    sb.append( '\'' );
256                    sb.append( sameField.get( 0 ) );
257                    sb.append( '\'' );
258                    sb.append( '\n' );
259                }
260                rsOffset += ftFetchContents.size();
261            }
262            return sb.toString();
263        }
264    }