diff options
| author | Samuel Tardieu <sam@rfc1149.net> | 2014-10-28 20:00:38 +0100 |
|---|---|---|
| committer | Samuel Tardieu <sam@rfc1149.net> | 2014-10-28 20:45:51 +0100 |
| commit | 60e270f098a4df471a278a04dd94632cb7753012 (patch) | |
| tree | ed6dcaaa3f8dca6143a959fd6698d7b60d80705b /main/src | |
| parent | 1babb0810e715f2e30d10f7bf14dd35fa88bfdbd (diff) | |
| download | cgeo-60e270f098a4df471a278a04dd94632cb7753012.zip cgeo-60e270f098a4df471a278a04dd94632cb7753012.tar.gz cgeo-60e270f098a4df471a278a04dd94632cb7753012.tar.bz2 | |
Use a XML pull parser to parse .loc files
Parsing XML with regular expressions is bad.
Diffstat (limited to 'main/src')
| -rw-r--r-- | main/src/cgeo/geocaching/files/LocParser.java | 173 |
1 files changed, 81 insertions, 92 deletions
diff --git a/main/src/cgeo/geocaching/files/LocParser.java b/main/src/cgeo/geocaching/files/LocParser.java index 13f8cca..4d8147a 100644 --- a/main/src/cgeo/geocaching/files/LocParser.java +++ b/main/src/cgeo/geocaching/files/LocParser.java @@ -9,34 +9,31 @@ import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.MatcherWrapper; +import org.apache.commons.io.Charsets; import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; 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.Pattern; public final class LocParser extends FileParser { private static final String NAME_OWNER_SEPARATOR = " by "; - 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=\"([^\"]+)\""); - private static final Pattern patternName = Pattern.compile("CDATA\\[([^\\]]+)\\]"); private static final CacheSize[] SIZES = { CacheSize.NOT_CHOSEN, // 1 @@ -49,10 +46,13 @@ public final class LocParser extends FileParser { CacheSize.SMALL, // 8 }; + // Used so that the initial value of the geocache is not null. Never filled. + private static final Geocache DUMMY_GEOCACHE = new Geocache(); + private int listId; public static void parseLoc(final SearchResult searchResult, final String fileContent) { - final Map<String, Geocache> cidCoords = parseCoordinates(fileContent); + final Map<String, Geocache> cidCoords = parseLoc(fileContent); // save found cache coordinates final HashSet<String> contained = new HashSet<>(); @@ -61,13 +61,76 @@ public final class LocParser extends FileParser { contained.add(geocode); } } - Set<Geocache> caches = DataStore.loadCaches(contained, LoadFlags.LOAD_CACHE_OR_DB); - for (Geocache cache : caches) { - Geocache coord = cidCoords.get(cache.getGeocode()); + final Set<Geocache> caches = DataStore.loadCaches(contained, LoadFlags.LOAD_CACHE_OR_DB); + for (final Geocache cache : caches) { + final Geocache coord = cidCoords.get(cache.getGeocode()); copyCoordToCache(coord, cache); } } + private static Map<String, Geocache> parseLoc(final String content) { + return parseLoc(new ByteArrayInputStream(content.getBytes(Charsets.UTF_8))); + } + + private static Map<String, Geocache> parseLoc(final InputStream content) { + try { + final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + final XmlPullParser xpp = factory.newPullParser(); + xpp.setInput(content, Charsets.UTF_8.name()); + final Map<String, Geocache> caches = new HashMap<>(); + int eventType = xpp.getEventType(); + Geocache currentCache = DUMMY_GEOCACHE; + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + switch (xpp.getName()) { + case "waypoint": + currentCache = new Geocache(); + currentCache.setType(CacheType.UNKNOWN); // Type not present in .loc file + break; + case "name": + currentCache.setGeocode(xpp.getAttributeValue(null, "id")); + if (xpp.next() == XmlPullParser.TEXT) { + final String nameOwner = xpp.getText(); + currentCache.setName(StringUtils.trim(StringUtils.substringBeforeLast(nameOwner, NAME_OWNER_SEPARATOR))); + currentCache.setOwnerUserId(StringUtils.trim(StringUtils.substringAfterLast(nameOwner, NAME_OWNER_SEPARATOR))); + } + break; + case "coord": + currentCache.setCoords(new Geopoint(Double.valueOf(xpp.getAttributeValue(null, "lat")), + Double.valueOf(xpp.getAttributeValue(null, "lon")))); + currentCache.setReliableLatLon(true); + break; + case "container": + if (xpp.next() == XmlPullParser.TEXT) { + currentCache.setSize(SIZES[Integer.valueOf(xpp.getText()) - 1]); + } + break; + case "difficulty": + if (xpp.next() == XmlPullParser.TEXT) { + currentCache.setDifficulty(Float.valueOf(xpp.getText())); + } + break; + case "terrain": + if (xpp.next() == XmlPullParser.TEXT) { + currentCache.setTerrain(Float.valueOf(xpp.getText())); + } + break; + default: + // Ignore + } + } else if (eventType == XmlPullParser.END_TAG && xpp.getName().equals("waypoint") && StringUtils.isNotBlank(currentCache.getGeocode())) { + caches.put(currentCache.getGeocode(), currentCache); + } + eventType = xpp.next(); + } + Log.d("Coordinates found in .loc content: " + caches.size()); + return caches; + } catch (XmlPullParserException | IOException e) { + Log.e("unable to parse .loc content", e); + return Collections.emptyMap(); + } + } + private static void copyCoordToCache(final Geocache coord, final Geocache cache) { cache.setCoords(coord.getCoords()); cache.setDifficulty(coord.getDifficulty()); @@ -81,27 +144,6 @@ public final class LocParser extends FileParser { cache.setOwnerUserId(coord.getOwnerUserId()); } - static Map<String, Geocache> parseCoordinates(final String fileContent) { - final Map<String, Geocache> coords = new HashMap<>(); - if (StringUtils.isBlank(fileContent)) { - return coords; - } - // >> premium only - - final String[] points = StringUtils.splitByWholeSeparator(fileContent, "<waypoint>"); - - // parse coordinates - for (String pointString : points) { - final Geocache 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 { @@ -119,18 +161,14 @@ public final class LocParser extends FileParser { @Override public Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException { - final String streamContent = readStream(stream, null).toString(); - final int maxSize = streamContent.length(); - final Map<String, Geocache> coords = parseCoordinates(streamContent); + final int maxSize = stream.available(); + final Map<String, Geocache> coords = parseLoc(stream); final List<Geocache> caches = new ArrayList<>(); - for (Entry<String, Geocache> entry : coords.entrySet()) { - Geocache coord = entry.getValue(); - if (StringUtils.isBlank(coord.getGeocode()) || StringUtils.isBlank(coord.getName())) { + for (final Entry<String, Geocache> entry : coords.entrySet()) { + final Geocache cache = entry.getValue(); + if (StringUtils.isBlank(cache.getGeocode()) || StringUtils.isBlank(cache.getName())) { continue; } - Geocache cache = new Geocache(); - cache.setReliableLatLon(true); - copyCoordToCache(coord, cache); caches.add(cache); fixCache(cache); @@ -146,53 +184,4 @@ public final class LocParser extends FileParser { return caches; } - public static Geocache parseCache(final String pointString) { - final Geocache cache = new Geocache(); - final MatcherWrapper matcherGeocode = new MatcherWrapper(patternGeocode, pointString); - if (matcherGeocode.find()) { - cache.setGeocode(matcherGeocode.group(1).trim()); - } - - final MatcherWrapper matcherName = new MatcherWrapper(patternName, pointString); - if (matcherName.find()) { - final String name = matcherName.group(1).trim(); - String ownerName = StringUtils.trim(StringUtils.substringAfterLast(name, NAME_OWNER_SEPARATOR)); - if (StringUtils.isEmpty(cache.getOwnerUserId()) && StringUtils.isNotEmpty(ownerName)) { - cache.setOwnerUserId(ownerName); - } - cache.setName(StringUtils.substringBeforeLast(name, NAME_OWNER_SEPARATOR).trim()); - } else { - cache.setName(cache.getGeocode()); - } - - final MatcherWrapper matcherLat = new MatcherWrapper(patternLat, pointString); - final MatcherWrapper matcherLon = new MatcherWrapper(patternLon, pointString); - if (matcherLat.find() && matcherLon.find()) { - cache.setCoords(parsePoint(matcherLat.group(1).trim(), matcherLon.group(1).trim())); - } - - final String difficulty = StringUtils.substringBetween(pointString, "<difficulty>", "</difficulty>"); - final String terrain = StringUtils.substringBetween(pointString, "<terrain>", "</terrain>"); - final String container = StringUtils.substringBetween(pointString, "<container>", "</container"); - try { - if (StringUtils.isNotBlank(difficulty)) { - cache.setDifficulty(Float.parseFloat(difficulty.trim())); - } - - if (StringUtils.isNotBlank(terrain)) { - cache.setTerrain(Float.parseFloat(terrain.trim())); - } - - if (StringUtils.isNotBlank(container)) { - final int size = Integer.parseInt(container.trim()); - if (size >= 1 && size <= 8) { - cache.setSize(SIZES[size - 1]); - } - } - } catch (NumberFormatException e) { - Log.e("LocParser.parseCache", e); - } - - return cache; - } } |
