/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2009, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.aggregate; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.data.DataStore; import org.geotools.data.DataUtilities; import org.geotools.data.Query; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.store.EmptyFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.filter.FilterAttributeExtractor; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; class FeatureCallable implements Callable<Void> { static final Logger LOGGER = Logging.getLogger(FeatureCallable.class); Query query; AggregatingDataStore store; Name storeName; String typeName; FeatureQueue queue; SimpleFeatureBuilder builder; private boolean stopped = false; public FeatureCallable(AggregatingDataStore store, Query query, Name storeName, String typeName, FeatureQueue queue, SimpleFeatureType target) { super(); this.store = store; this.query = query; this.storeName = storeName; this.typeName = typeName; this.queue = queue; this.builder = new SimpleFeatureBuilder(target); } @Override public Void call() throws Exception { SimpleFeatureIterator fi = null; int storeId = -1; try { // get the feature list DataStore ds = store.getStore(storeName, store.isTolerant()); AggregateTypeConfiguration config = store.getConfigurations().get( builder.getFeatureType().getTypeName()); storeId = config.getStoreIndex(storeName); SimpleFeatureSource source = ds.getFeatureSource(typeName); Query q = new Query(query); q.setTypeName(typeName); q.setSortBy(null); Filter originalFilter = q.getFilter(); if (originalFilter != null && !Filter.INCLUDE.equals(originalFilter)) { // eliminate the extra attribute the delegate source does not know about FilterAttributeExtractor extractor = new FilterAttributeExtractor(); originalFilter.accept(extractor, null); Set<String> filterNames = extractor.getAttributeNameSet(); Set<String> sourceNames = getSourceAttributes(source); if (!sourceNames.containsAll(filterNames)) { MissingPropertiesEraser eraser = new MissingPropertiesEraser(sourceNames); Filter erased = (Filter) originalFilter.accept(eraser, null); q.setFilter(erased); } } // eliminate the extra attributes the delegate source does not know about fixupProperties(q, builder.getFeatureType(), getSourceAttributes(source)); fi = source.getFeatures(q).features(); // put every item in the queue, including the exception while (fi.hasNext() && !stopped) { SimpleFeature feature = fi.next(); // build the retyped feature, with a unique id for (AttributeDescriptor ad : builder.getFeatureType().getAttributeDescriptors()) { String attribute = ad.getLocalName(); Object attValue = feature.getAttribute(attribute); if (attValue != null) { builder.set(attribute, attValue); } } String id = feature.getID(); if (id.startsWith(typeName)) { id = id.substring(typeName.length() + 1); } id = builder.getFeatureType().getTypeName() + "." + storeId + "." + id; SimpleFeature sf = builder.buildFeature(id); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Adding a new feature {0} from store {1}", new Object[] { sf, storeId }); } queue.put(sf); } } catch (Exception e) { String message = "Failed to retrieve features on " + storeName + "/" + typeName; if (store.isTolerant()) { AggregatingDataStore.LOGGER.log(Level.WARNING, message, e); return null; } else { queue.setException(e); } } finally { LOGGER.log(Level.FINE, "Adding the end marker for store {0}", storeId); queue.sourceComplete(this); queue.put(FeatureQueue.END_MARKER); if (fi != null) { fi.close(); } } return null; } void fixupProperties(Query q, SimpleFeatureType featureType, Set<String> sourceNames) { if(q.getPropertyNames() != null) { if(q.getPropertyNames().length > 0) { // some specific property was asked for, let's filter them List<String> filtered = new ArrayList<String>(); for (String name : q.getPropertyNames()) { if(sourceNames.contains(name)) { filtered.add(name); } } String[] filteredArray = (String[]) filtered.toArray(new String[filtered.size()]); q.setPropertyNames(filteredArray); } } else { // all attributed were asked for, let's stick to the intersection between // target and source then List<String> filtered = new ArrayList<String>(); for (String name : sourceNames) { if(featureType.getDescriptor(name) != null) { filtered.add(name); } } String[] filteredArray = (String[]) filtered.toArray(new String[filtered.size()]); q.setPropertyNames(filteredArray); } } /** * Collects the attribute names for the specified feature source * * @param source * @return */ private Set<String> getSourceAttributes(SimpleFeatureSource source) { Set<String> result = new HashSet<String>(); for (AttributeDescriptor ad : source.getSchema().getAttributeDescriptors()) { result.add(ad.getLocalName()); } return result; } /** * Stops the callable where it is */ public void shutdown() { stopped = true; } }