content: package aimax.osm.data.impl;
  
  import java.util.List;
  import java.util.StringTokenizer;
  
  import aimax.osm.data.BoundingBox;
  import aimax.osm.data.OsmMap;
  import aimax.osm.data.Position;
  import aimax.osm.data.entities.EntityAttribute;
  import aimax.osm.data.entities.MapEntity;
  import aimax.osm.data.entities.MapNode;
  import aimax.osm.data.entities.MapWay;
  
  public class DefaultEntityFinder extends AbstractEntityFinder {
  
  	public DefaultEntityFinder(OsmMap storage) {
  		super(storage);
  	}
  
  	/**
  	 * Searches for entities which comply to the current search specification
  	 * and stores them as results.
  	 */
  	@Override
  	protected void find(boolean findMore) {
  		BestMatchFinder bmf = new BestMatchFinder(pattern);
  		List<MapEntity> results = getResults();
  		BoundingBox bb = new BoundingBox(position, nextRadius);
  		if (!results.isEmpty())
  			bmf.checkMatchQuality(results.get(0));
  		if (mode.equals(Mode.ENTITY) || mode.equals(Mode.NODE)) {
  			for (MapNode node : getStorage().getPois(bb)) {
  				int match = bmf.checkMatchQuality(node);
  				if (match >= 0) {
  					if (match > 0) {
  						results.clear();
  						bmf.useAsReference(node);
  					}
  					if (position.insertInAscendingDistanceOrder(results, node))
  						if (results.size() > 100)
  							results.remove(99);
  				}
  			}
  		}
  		if (mode.equals(Mode.ENTITY) || mode.equals(Mode.WAY)) {
  			for (MapWay way : getStorage().getWays(bb)) {
  				int match = bmf.checkMatchQuality(way);
  				if (match >= 0) {
  					if (match > 0) {
  						results.clear();
  						bmf.useAsReference(way);
  					}
  					if (position.insertInAscendingDistanceOrder(results, way))
  						if (results.size() > 100)
  							results.remove(99);
  				}
  			}
  		}
  		
  		if (mode.equals(Mode.ADDRESS)) {
  			List<MapEntity> iResults = getIntermediateResults();
  			StringTokenizer tokenizer = new StringTokenizer(pattern, ",");
  			String placeName = null;
  			String wayName = null;
  			if (tokenizer.hasMoreElements())
  				placeName = tokenizer.nextToken();
  			if (tokenizer.hasMoreElements())
  				wayName = tokenizer.nextToken().trim();
  
  			if (placeName != null && !findMore) {
  				for (MapNode place : getStorage().getPlaces(placeName)) {
  					position.insertInAscendingDistanceOrder(
  							iResults, place);
  					if (iResults.size() > 100)
  						iResults.remove(99);
  				}
  				nextRadius = -1;
  			}
  			if (iResults.size() == 1 && wayName != null) {
  				MapNode place = (MapNode) iResults.get(0);
  				findWay(wayName, new Position(place.getLat(), place.getLon()),
  						null);
  			}
  			
  		} else {
  			nextRadius *= 2;
  			if (results.isEmpty() && getIntermediateResults().isEmpty() && nextRadius <= getMaxRadius())
  				find(true);
  		}
  	}
  
  	/**
  	 * Helper class which is used to find the best match when searching for
  	 * special entities.
  	 * 
  	 * @author Ruediger Lunde
  	 */
  	private static class BestMatchFinder {
  		/** String, specifying the entity to be searched for. */
  		String searchPattern;
  		/**
  		 * Value between 1 and 5 which classifies the reference match level. 1:
  		 * reference entity name equal to pattern; 2: reference entity has
  		 * attribute value which is identical with the pattern; 3: reference
  		 * entity has attribute name equal to pattern; 4: reference entity name
  		 * contains pattern; 5: no match found.
  		 */
  		int currMatchLevel = 5;
  
  		/** Creates a match finder for a given search pattern. */
  		protected BestMatchFinder(String pattern) {
  			searchPattern = pattern.toLowerCase();
  		}
  
  		/**
  		 * Compares whether a given entity matches the search pattern better or
  		 * worse than the previously defined reference entity.
  		 * 
  		 * @return Match level (-1: forget new entity, 0: new entity match
  		 *         quality equal to reference entity, 1: new entity matches
  		 *         better then reference entity)
  		 */
  		protected int checkMatchQuality(MapEntity entity) {
  			int matchLevel = getMatchLevel(entity);
  			int result = -1;
  			if (matchLevel < 5) {
  				if (matchLevel < currMatchLevel)
  					result = 1;
  				else if (matchLevel == currMatchLevel) {
  					result = 0;
  				}
  			}
  			return result;
  		}
  
  		/** Defines a new reference entity for search pattern match checks. */
  		protected void useAsReference(MapEntity entity) {
  			currMatchLevel = getMatchLevel(entity);
  		}
  
  		private int getMatchLevel(MapEntity entity) {
  			String name = entity.getName();
  			if (name != null)
  				name = name.toLowerCase();
  			if (name != null && name.equals(searchPattern))
  				return 1;
  			if (currMatchLevel >= 2) {
  				for (EntityAttribute att : entity.getAttributes())
  					if (att.getValue().toLowerCase().equals(searchPattern))
  						return 2;
  			}
  			if (currMatchLevel >= 3) {
  				for (EntityAttribute att : entity.getAttributes())
  					if (att.getKey().toLowerCase().equals(searchPattern))
  						return 3;
  			}
  			if (name != null && currMatchLevel >= 4
  					&& name.contains(searchPattern))
  				return 4;
  			return 5;
  		}
  	}
  }
  