protected ContentFeatureCollection(ContentFeatureSource featureSource, Query query) {
    this.featureSource = featureSource;
    this.query = query;

    // retype feature type if necessary
    if (query.getPropertyNames() != Query.ALL_NAMES) {
      this.featureType =
          SimpleFeatureTypeBuilder.retype(featureSource.getSchema(), query.getPropertyNames());
    } else {
      this.featureType = featureSource.getSchema();
    }
  }
  public ReferencedEnvelope getBounds() {
    FeatureReader<SimpleFeatureType, SimpleFeature> reader = null;
    try {
      ReferencedEnvelope result = featureSource.getBounds(query);
      if (result != null) {
        return result;
      }

      // ops, we have to compute the results by hand. Let's load just the
      // geometry attributes though
      Query q = new Query(query);
      List<String> geometries = new ArrayList<String>();
      for (AttributeDescriptor ad : getSchema().getAttributeDescriptors()) {
        if (ad instanceof GeometryDescriptor) {
          geometries.add(ad.getLocalName());
        }
      }
      // no geometries, no bounds
      if (geometries.size() == 0) {
        return new ReferencedEnvelope();
      } else {
        q.setPropertyNames(geometries);
      }
      // grab the features and scan through them
      reader = featureSource.getReader(q);
      while (reader.hasNext()) {
        SimpleFeature f = reader.next();
        ReferencedEnvelope featureBounds = ReferencedEnvelope.reference(f.getBounds());
        if (result == null) {
          result = featureBounds;
        } else if (featureBounds != null) {
          result.expandToInclude(featureBounds);
        }
      }
      // return the results if we got any, or return an empty one otherwise
      if (result != null) {
        return result;
      } else {
        return new ReferencedEnvelope(getSchema().getCoordinateReferenceSystem());
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException ex) {
          // we tried...
        }
      }
    }
  }
 public Iterator iterator() {
   try {
     return new WrappingIterator(featureSource.getReader(query));
   } catch (IOException e) {
     throw new RuntimeException(e);
   }
 }
  /**
   * Removes a listener for collection events.
   *
   * @param listener The listener to remove
   */
  public void removeListener(CollectionListener listener) {
    // as soon as the listeners are out we clean up
    synchronized (listeners) {
      listeners.remove(listener);

      if (listeners.size() == 0) featureSource.removeFeatureListener(this.listener);
    }
  }
  /**
   * Adds a listener for collection events.
   *
   * @param listener The listener to add
   */
  public void addListener(CollectionListener listener) {
    // create the bridge only if we have collection listeners around
    synchronized (listeners) {
      if (listeners.size() == 0) {
        featureSource.addFeatureListener(this.listener);
      }

      listeners.add(listener);
    }
  }
 public int size() {
   FeatureReader fr = null;
   try {
     int size = featureSource.getCount(query);
     if (size >= 0) {
       return size;
     } else {
       // we have to iterate, probably best if we do a minimal query that
       // only loads a short attribute
       AttributeDescriptor chosen = null;
       for (AttributeDescriptor ad : getSchema().getAttributeDescriptors()) {
         if (chosen == null || size(ad) < size(chosen)) {
           chosen = ad;
         }
       }
       // build the minimal query
       Query q = new Query(query);
       if (chosen != null) {
         q.setPropertyNames(Collections.singletonList(chosen.getLocalName()));
       }
       // bean counting...
       fr = featureSource.getReader(q);
       int count = 0;
       while (fr.hasNext()) {
         fr.next();
         count++;
       }
       return count;
     }
   } catch (IOException e) {
     throw new RuntimeException(e);
   } finally {
     if (fr != null) {
       try {
         fr.close();
       } catch (IOException e) {
         throw new RuntimeException(e);
       }
     }
   }
 }
 // Visitors
 public void accepts(
     org.opengis.feature.FeatureVisitor visitor, org.opengis.util.ProgressListener progress)
     throws IOException {
   featureSource.accepts(query, visitor, progress);
 }