content: package aimax.osm.reader;
  
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.util.List;
  
  import aimax.osm.data.BoundingBox;
  import aimax.osm.data.EntityClassifier;
  import aimax.osm.data.MapBuilder;
  import aimax.osm.data.MapBuilderProxy;
  import aimax.osm.data.entities.EntityAttribute;
  import aimax.osm.data.impl.DefaultMapNode;
  import aimax.osm.data.impl.DefaultMapWay;
  
  public class FilteringOsmReader extends OsmReader {
  	BoundingBox boundingBox;
  	EntityClassifier<Boolean> attFilter;
  
  	/**
  	 * Sets a bounding box for the next read action from file. Map nodes which
  	 * are not inside will be ignored.
  	 */
  	public void setFilter(BoundingBox bb) {
  		boundingBox = bb;
  		attFilter = null;
  	}
  
  	/**
  	 * Sets an attribute filter for the next read action from file. Map entities
  	 * for which the classifier returns null will be ignored.
  	 */
  	public void setFilter(EntityClassifier<Boolean> attFilter) {
  		this.attFilter = attFilter;
  		boundingBox = null;
  	}
  
  	/**
  	 * Reads all data from the file and send it to the sink.
  	 */
  	public void readMap(File file, MapBuilder builder) {
  		try {
  			MapBuilderProxy proxy;
  			if (boundingBox != null)
  				proxy = new BBBuilderProxy(builder, boundingBox);
  			else if (attFilter != null)
  				proxy = new FilteringBuilderProxy(builder, attFilter);
  			else
  				proxy = new MapBuilderProxy(builder);
  			parseMap(createFileStream(file), proxy);
  			if (proxy.nodeRefsWithoutDefsAdded()) {
  				if (boundingBox != null || attFilter != null) {
  					LOG.info("Starting to parse the map file a second time.");
  					proxy.incrementCounter();
  					parseMap(createFileStream(file), proxy);
  				} else {
  					LOG
  							.warning("Nodes were referenced in ways but not defined before.");
  				}
  			}
  		} catch (FileNotFoundException e) {
  			LOG.warning("File does not exist " + file);
  		} catch (Exception e) {
  			LOG.warning("The map could not be read. " + e);
  			e.printStackTrace();
  		} finally {
  			boundingBox = null;
  			attFilter = null;
  		}
  	}
  
  	// ////////////////////////////////////////////////////////////////////
  	// inner classes
  
  	/** Builder proxy used for bounding box filtering. */
  	private static class BBBuilderProxy extends MapBuilderProxy {
  		BoundingBox bb;
  
  		protected BBBuilderProxy(MapBuilder builder, BoundingBox bb) {
  			super(builder);
  			this.bb = bb;
  			builder.setBoundingBox(bb);
  		}
  
  		/** Does nothing. */
  		@Override
  		public void setBoundingBox(BoundingBox bb) {
  		}
  
  		@Override
  		public void addNode(long id, String name, List<EntityAttribute> atts,
  				float lat, float lon) {
  			if (counter == 0 && lat >= bb.getLatMin() && lat <= bb.getLatMax()
  					&& lon >= bb.getLonMin() && lon <= bb.getLonMax())
  				super.addNode(id, name, atts, lat, lon);
  			else if (counter == 1 && super.isNodeReferenced(id))
  				super.addNode(id, name, atts, lat, lon);
  		}
  
  		@Override
  		public void addWay(long id, String name, List<EntityAttribute> atts,
  				List<Long> wayNodeIds) {
  			if (counter == 0) {
  				for (long nodeId : wayNodeIds)
  					if (builder.isNodeDefined(nodeId, bb)) {
  						super.addWay(id, name, atts, wayNodeIds);
  						break;
  					}
  			}
  		}
  	}
  
  	/** Builder proxy used for attribute filtering. */
  	private static class FilteringBuilderProxy extends
  			aimax.osm.data.MapBuilderProxy {
  		EntityClassifier<Boolean> attFilter;
  
  		protected FilteringBuilderProxy(MapBuilder builder,
  				EntityClassifier<Boolean> attFilter) {
  			super(builder);
  			this.attFilter = attFilter;
  		}
  
  		@Override
  		public void addNode(long id, String name, List<EntityAttribute> atts,
  				float lat, float lon) {
  			if (counter == 0) {
  				DefaultMapNode node = new DefaultMapNode(id);
  				node.setAttributes(atts);
  				if (attFilter.classify(node) != null)
  					super.addNode(id, name, atts, lat, lon);
  			} else if (counter == 1 && super.isNodeReferenced(id))
  				super.addNode(id, name, atts, lat, lon);
  		}
  
  		@Override
  		public void addWay(long id, String name, List<EntityAttribute> atts,
  				List<Long> wayNodeIds) {
  			if (counter == 0) {
  				DefaultMapWay way = new DefaultMapWay(id);
  				way.setAttributes(atts);
  				if (attFilter.classify(way) != null) {
  					super.addWay(id, name, atts, wayNodeIds);
  				}
  			}
  		}
  	}
  }
  