package cgeo.geocaching.files; import cgeo.geocaching.SearchResult; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class LocParser extends FileParser { private static final Pattern patternGeocode = Pattern .compile("name id=\"([^\"]+)\""); private static final Pattern patternLat = Pattern .compile("lat=\"([^\"]+)\""); private static final Pattern patternLon = Pattern .compile("lon=\"([^\"]+)\""); // premium only >> private static final Pattern patternDifficulty = Pattern .compile("([^<]+)"); private static final Pattern patternTerrain = Pattern .compile("([^<]+)"); private static final Pattern patternContainer = Pattern .compile("([^<]+)"); private static final Pattern patternName = Pattern.compile("CDATA\\[([^\\]]+)\\]"); private static final CacheSize[] SIZES = { CacheSize.NOT_CHOSEN, // 1 CacheSize.MICRO, // 2 CacheSize.REGULAR, // 3 CacheSize.LARGE, // 4 CacheSize.VIRTUAL, // 5 CacheSize.OTHER, // 6 CacheSize.UNKNOWN, // 7 CacheSize.SMALL, // 8 }; private int listId; public static void parseLoc(final SearchResult searchResult, final String fileContent) { final Map cidCoords = parseCoordinates(fileContent); // save found cache coordinates final HashSet contained = new HashSet(); for (String geocode : searchResult.getGeocodes()) { if (cidCoords.containsKey(geocode)) { contained.add(geocode); } } Set caches = cgeoapplication.getInstance().loadCaches(contained, LoadFlags.LOAD_CACHE_OR_DB); for (cgCache cache : caches) { cgCache coord = cidCoords.get(cache.getGeocode()); copyCoordToCache(coord, cache); } } private static void copyCoordToCache(final cgCache coord, final cgCache cache) { cache.setCoords(coord.getCoords()); cache.setDifficulty(coord.getDifficulty()); cache.setTerrain(coord.getTerrain()); cache.setSize(coord.getSize()); cache.setGeocode(coord.getGeocode().toUpperCase()); cache.setReliableLatLon(true); if (StringUtils.isBlank(cache.getName())) { cache.setName(coord.getName()); } } static Map parseCoordinates(final String fileContent) { final Map coords = new HashMap(); if (StringUtils.isBlank(fileContent)) { return coords; } // >> premium only final String[] points = fileContent.split(""); // parse coordinates for (String pointString : points) { final cgCache pointCoord = parseCache(pointString); if (StringUtils.isNotBlank(pointCoord.getGeocode())) { coords.put(pointCoord.getGeocode(), pointCoord); } } Log.i("Coordinates found in .loc file: " + coords.size()); return coords; } public static Geopoint parsePoint(final String latitude, final String longitude) { // the loc file contains the coordinates as plain floating point values, therefore avoid using the GeopointParser try { return new Geopoint(Double.valueOf(latitude), Double.valueOf(longitude)); } catch (NumberFormatException e) { Log.e("LOC format has changed"); } // fall back to parser, just in case the format changes return new Geopoint(latitude, longitude); } public LocParser(int listId) { this.listId = listId; } @Override public Collection parse(InputStream stream, CancellableHandler progressHandler) throws IOException, ParserException { // TODO: progress reporting happens during reading stream only, not during parsing String streamContent = readStream(stream, progressHandler).toString(); final Map coords = parseCoordinates(streamContent); final List caches = new ArrayList(); for (Entry entry : coords.entrySet()) { cgCache coord = entry.getValue(); if (StringUtils.isBlank(coord.getGeocode()) || StringUtils.isBlank(coord.getName())) { continue; } cgCache cache = new cgCache(); cache.setReliableLatLon(true); copyCoordToCache(coord, cache); caches.add(cache); fixCache(cache); cache.setType(CacheType.UNKNOWN); // type is not given in the LOC file cache.setListId(listId); cache.setDetailed(true); } Log.i("Caches found in .loc file: " + caches.size()); return caches; } public static cgCache parseCache(final String pointString) { final cgCache cache = new cgCache(); final Matcher matcherGeocode = patternGeocode.matcher(pointString); if (matcherGeocode.find()) { final String geocode = matcherGeocode.group(1).trim().toUpperCase(); cache.setGeocode(geocode.toUpperCase()); } final Matcher matcherName = patternName.matcher(pointString); if (matcherName.find()) { final String name = matcherName.group(1).trim(); cache.setName(StringUtils.substringBeforeLast(name, " by ").trim()); } else { cache.setName(cache.getGeocode()); } final Matcher matcherLat = patternLat.matcher(pointString); final Matcher matcherLon = patternLon.matcher(pointString); if (matcherLat.find() && matcherLon.find()) { cache.setCoords(parsePoint(matcherLat.group(1).trim(), matcherLon.group(1).trim())); } final Matcher matcherDifficulty = patternDifficulty.matcher(pointString); if (matcherDifficulty.find()) { cache.setDifficulty(Float.parseFloat(matcherDifficulty.group(1).trim())); } final Matcher matcherTerrain = patternTerrain.matcher(pointString); if (matcherTerrain.find()) { cache.setTerrain(Float.parseFloat(matcherTerrain.group(1).trim())); } final Matcher matcherContainer = patternContainer.matcher(pointString); if (matcherContainer.find()) { final int size = Integer.parseInt(matcherContainer.group(1).trim()); if (size >= 1 && size <= 8) { cache.setSize(SIZES[size - 1]); } } return cache; } }