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 }