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 }