package cgeo.geocaching.files; import cgeo.geocaching.SearchResult; import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgCoord; 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.geopoint.GeopointParser; import cgeo.geocaching.utils.CancellableHandler; import org.apache.commons.lang3.StringUtils; import android.util.Log; 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 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) { cgCoord coord = cidCoords.get(cache.getGeocode()); copyCoordToCache(coord, cache); } } private static void copyCoordToCache(final cgCoord 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 cgCoord pointCoord = new cgCoord(); final Matcher matcherGeocode = patternGeocode.matcher(pointString); if (matcherGeocode.find()) { String geocode = matcherGeocode.group(1).trim().toUpperCase(); pointCoord.setName(geocode); pointCoord.setGeocode(geocode); } final Matcher matcherName = patternName.matcher(pointString); if (matcherName.find()) { String name = matcherName.group(1).trim(); pointCoord.setName(StringUtils.substringBeforeLast(name, " by ").trim()); // owner = StringUtils.substringAfterLast(" by ").trim(); } final Matcher matcherLat = patternLat.matcher(pointString); final Matcher matcherLon = patternLon.matcher(pointString); if (matcherLat.find() && matcherLon.find()) { pointCoord.setCoords(parsePoint(matcherLat.group(1).trim(), matcherLon.group(1).trim())); } final Matcher matcherDifficulty = patternDifficulty.matcher(pointString); if (matcherDifficulty.find()) { pointCoord.setDifficulty(Float.parseFloat(matcherDifficulty.group(1).trim())); } final Matcher matcherTerrain = patternTerrain.matcher(pointString); if (matcherTerrain.find()) { pointCoord.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) { pointCoord.setSize(CacheSize.NOT_CHOSEN); } else if (size == 2) { pointCoord.setSize(CacheSize.MICRO); } else if (size == 3) { pointCoord.setSize(CacheSize.REGULAR); } else if (size == 4) { pointCoord.setSize(CacheSize.LARGE); } else if (size == 5) { pointCoord.setSize(CacheSize.VIRTUAL); } else if (size == 6) { pointCoord.setSize(CacheSize.OTHER); } else if (size == 8) { pointCoord.setSize(CacheSize.SMALL); } else { pointCoord.setSize(CacheSize.UNKNOWN); } } if (StringUtils.isNotBlank(pointCoord.getGeocode())) { coords.put(pointCoord.getGeocode(), pointCoord); } } Log.i(Settings.tag, "Coordinates found in .loc file: " + coords.size()); return coords; } private static Geopoint parsePoint(String latitude, 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(Settings.tag, "LOC format has changed"); } // fall back to parser, just in case the format changes return GeopointParser.parse(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()) { cgCoord 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(Settings.tag, "Caches found in .loc file: " + caches.size()); return caches; } }