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 }