From: <svn...@os...> - 2012-03-25 09:40:57
|
Author: aaime Date: 2012-03-25 02:40:50 -0700 (Sun, 25 Mar 2012) New Revision: 38643 Modified: trunk/modules/library/main/src/main/java/org/geotools/feature/collection/SubFeatureList.java Log: [GEOT-3244] NPE raised from SubFeatureList when sorting Modified: trunk/modules/library/main/src/main/java/org/geotools/feature/collection/SubFeatureList.java =================================================================== --- trunk/modules/library/main/src/main/java/org/geotools/feature/collection/SubFeatureList.java 2012-03-24 20:27:03 UTC (rev 38642) +++ trunk/modules/library/main/src/main/java/org/geotools/feature/collection/SubFeatureList.java 2012-03-25 09:40:50 UTC (rev 38643) @@ -26,7 +26,9 @@ import java.util.Set; import org.geotools.data.simple.SimpleFeatureCollection; +import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.factory.CommonFactoryFinder; +import org.geotools.util.SoftValueHashMap; import org.opengis.feature.simple.SimpleFeature; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; @@ -54,16 +56,16 @@ public SubFeatureList(SimpleFeatureCollection list, SortBy sort ){ this( list, Filter.INCLUDE, sort ); } + /** - * Create a simple SubFeatureList with the provided - * filter. - * - * @param filter - */ - public SubFeatureList(SimpleFeatureCollection list, Filter filter, SortBy subSort) { - super( list, filter ); - - if( subSort == null || subSort.equals( SortBy.NATURAL_ORDER ) ){ + * Create a simple SubFeatureList with the provided filter. + * + * @param filter + */ + public SubFeatureList(SimpleFeatureCollection list, Filter filter, SortBy subSort) { + super(list, filter); + + if( subSort == null || subSort.equals( SortBy.NATURAL_ORDER ) ) { sort = Collections.emptyList(); } else { sort = new ArrayList<SortBy>(); @@ -73,7 +75,7 @@ } sort.add( subSort ); } - index = null; + index = createIndex(); } public SubFeatureList(SimpleFeatureCollection list, List order) { @@ -92,32 +94,31 @@ * @throws IndexOutOfBoundsException * if index is not between 0 and size */ - public SimpleFeature get( int position ) { + public SimpleFeature get(int position) { + FeatureId fid = index.get(position); if( collection instanceof RandomFeatureAccess){ RandomFeatureAccess random = (RandomFeatureAccess) collection; - FeatureId fid = index().get( position); return random.getFeatureMember( fid.getID() ); } - Iterator<SimpleFeature> it = iterator(); + SimpleFeatureIterator it = collection.features(); try { - for( int i=0; it.hasNext(); i++){ + while(it.hasNext()) { SimpleFeature feature = (SimpleFeature) it.next(); - if( i == position ){ + if(id.equals(feature.getID())) { return feature; } } throw new IndexOutOfBoundsException(); + } finally { + it.close(); } - finally { - close( it ); - } } /** Lazy create a filter based on index */ protected Filter createFilter() { FilterFactory ff = CommonFactoryFinder.getFilterFactory(null); Set featureIds = new HashSet(); - for(Iterator it = index().iterator(); it.hasNext();){ + for(Iterator it = index.iterator(); it.hasNext();){ featureIds.add(ff.featureId((String) it.next())); } Id fids = ff.id(featureIds); @@ -125,30 +126,24 @@ return fids; } - protected List<FeatureId> index(){ - if( index == null ){ - index = createIndex(); - } - return index; - } - /** Put this SubFeatureList in touch with its inner index */ - protected List<FeatureId> createIndex(){ + protected List<FeatureId> createIndex() { List<FeatureId> fids = new ArrayList<FeatureId>(); - Iterator<SimpleFeature> it = collection.iterator(); + SimpleFeatureIterator it = collection.features(); try { - while( it.hasNext() ){ + while(it.hasNext()) { SimpleFeature feature = it.next(); if( filter.evaluate(feature ) ){ fids.add( feature.getIdentifier() ); } } if( sort != null && !sort.isEmpty()){ - final SortBy initialOrder = (SortBy) sort.get( sort.size() -1 ); - Collections.sort( fids, new Comparator<FeatureId>(){ + final SortBy initialOrder = (SortBy) sort.get( sort.size() -1 ); + final FeatureIdAccessor idAccessor = new FeatureIdAccessor(true); + Collections.sort(fids, new Comparator<FeatureId>() { public int compare( FeatureId key1, FeatureId key2 ) { - SimpleFeature feature1 = getFeatureMember( key1.getID() ); - SimpleFeature feature2 = getFeatureMember( key2.getID() ); + SimpleFeature feature1 = idAccessor.getFeature( key1.getID() ); + SimpleFeature feature2 = idAccessor.getFeature( key2.getID() ); int compare = compare( feature1, feature2, initialOrder ); if( compare == 0 && sort.size() > 1 ){ @@ -158,23 +153,33 @@ } return compare; } + @SuppressWarnings("unchecked") protected int compare( SimpleFeature feature1, SimpleFeature feature2, SortBy order){ PropertyName name = order.getPropertyName(); Comparable value1 = (Comparable) name.evaluate( feature1 ); Comparable value2 = (Comparable) name.evaluate( feature2 ); - + + if(value1 == value2) { + return 0; + } if( order.getSortOrder() == SortOrder.ASCENDING ){ + if(value1 == null) { + return -1; + } return value1.compareTo( value2 ); + } else { + if(value2 == null) { + return -1; + } + return value2.compareTo( value1 ); } - else return value2.compareTo( value1 ); } }); } + } finally { + it.close(); } - finally { - collection.close( it ); - } return fids; } @@ -202,18 +207,19 @@ */ public boolean add(SimpleFeature feature) { boolean added = collection.add( feature ); - if( added ){ - index().add( feature.getIdentifier() ); + if(added){ + index.add( feature.getIdentifier() ); } return true; } - public int indexOf(SimpleFeature feature) { - return index().indexOf( feature.getIdentifier() ); - } - public int lastIndexOf(SimpleFeature feature) { - return index().lastIndexOf( feature.getIdentifier() ); - } + public int indexOf(SimpleFeature feature) { + return index.indexOf(feature.getIdentifier()); + } + + public int lastIndexOf(SimpleFeature feature) { + return index.lastIndexOf(feature.getIdentifier()); + } // // Feature Collection methods @@ -234,21 +240,19 @@ } return new SubFeatureList(collection, ff.and( filter, subfilter), sort.get(0) ); } + // // RandomFeatureAccess // public SimpleFeature getFeatureMember( String id ) throws NoSuchElementException { - int position = index.indexOf( id ); - if( position == -1){ + int position = index.indexOf(ff.featureId(id)); + if( position == -1) { throw new NoSuchElementException(id); } - if( collection instanceof RandomFeatureAccess ){ - RandomFeatureAccess random = (RandomFeatureAccess) collection; - random.getFeatureMember( id ); - } - return (SimpleFeature) get( position ); + return new FeatureIdAccessor(false).getFeature(id); } + public SimpleFeature removeFeatureMember( String id ) { int position = index.indexOf( ff.featureId(id) ); if( position == -1){ @@ -261,26 +265,17 @@ } return (SimpleFeature) remove( position ); } - public SimpleFeature remove( int position ){ + + public SimpleFeature remove(int position) { + FeatureId fid = index.get(position); if( collection instanceof RandomFeatureAccess){ RandomFeatureAccess random = (RandomFeatureAccess) collection; - FeatureId fid = index().get( position ); + return random.removeFeatureMember( fid.getID() ); } - Iterator<SimpleFeature> it = iterator(); - try { - for( int i=0; it.hasNext(); i++){ - SimpleFeature feature = (SimpleFeature) it.next(); - if( i == position ){ - collection.remove( feature ); - return feature; - } - } - throw new IndexOutOfBoundsException(); - } - finally { - close( it ); - } + SimpleFeature feature = new FeatureIdAccessor(false).getFeature(id); + collection.remove(feature); + return feature; } /** @@ -298,20 +293,78 @@ } private class SortedIteratory implements Iterator<SimpleFeature> { - Iterator<FeatureId> iterator = index().iterator(); + Iterator<FeatureId> iterator = index.iterator(); String id; + FeatureIdAccessor idAccessor = new FeatureIdAccessor(true); + public boolean hasNext() { return iterator != null && iterator.hasNext(); } public SimpleFeature next() { FeatureId fid = iterator.next(); id = fid.getID(); - return getFeatureMember( id ); + return idAccessor.getFeature(id); } public void remove() { removeFeatureMember(id); } } + + private class FeatureIdAccessor { + SoftValueHashMap<String, SimpleFeature> featureCache; + private boolean cacheFeatures; + + public FeatureIdAccessor(boolean cacheFeatures) { + this.cacheFeatures = cacheFeatures; + if(cacheFeatures) { + featureCache = new SoftValueHashMap<String, SimpleFeature>(); + } + } + + protected SimpleFeature getFeature(String id) { + if(collection instanceof RandomFeatureAccess) { + RandomFeatureAccess random = (RandomFeatureAccess) collection; + return random.getFeatureMember( id ); + } else if(cacheFeatures) { + // check in the cache + SimpleFeature result = featureCache.get(id); + if(result != null) { + return result; + } + + // sigh, full scan needed + SimpleFeatureIterator it = collection.features(); + try { + while(it.hasNext()) { + SimpleFeature feature = it.next(); + featureCache.put(id, feature); + if(id.equals(feature.getID())) { + return feature; + } + } + } finally { + it.close(); + } + + throw new RuntimeException("Could not find feature with id " + id); + } else { + // full scan... + SimpleFeatureIterator it = collection.features(); + try { + while(it.hasNext()) { + SimpleFeature feature = it.next(); + if(id.equals(feature.getID())) { + return feature; + } + } + } finally { + it.close(); + } + + throw new RuntimeException("Could not find feature with id " + id); + } + } + } } |