package cgeo.geocaching.connector.trackable; import cgeo.geocaching.log.LogEntry; import cgeo.geocaching.log.LogType; import cgeo.geocaching.models.Trackable; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.SynchronizedDateFormat; import org.apache.commons.lang3.StringUtils; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import android.support.annotation.NonNull; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; public class GeolutinsParser { private GeolutinsParser() { // Utility class } static class GeolutinsHandler extends DefaultHandler { private static final SynchronizedDateFormat DATE_FORMAT = new SynchronizedDateFormat("dd/MM/yyyy", TimeZone.getTimeZone("UTC"), Locale.US); private final List<Trackable> trackables = new ArrayList<>(); private Trackable trackable; private LogEntry.Builder logEntryBuilder; private final List<LogEntry> logsEntries = new ArrayList<>(); private String content; private boolean isMultiline = false; private boolean isInApparition; @NonNull public final List<Trackable> getTrackables() { return trackables; } @Override public final void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { content = ""; if (localName.equalsIgnoreCase("geolutin")) { trackable = new Trackable(); trackable.forceSetBrand(TrackableBrand.GEOLUTINS); trackables.add(trackable); trackable.setSpottedType(Trackable.SPOTTED_OWNER); } if (localName.equalsIgnoreCase("apparition_disparition")) { logEntryBuilder = new LogEntry.Builder(); isInApparition = true; } if (localName.equalsIgnoreCase("description")) { isMultiline = true; } } @Override public final void endElement(final String uri, final String localName, final String qName) throws SAXException { try { if (localName.equalsIgnoreCase("geolutin_id")) { trackable.setGeocode(content); } if (localName.equalsIgnoreCase("nom")) { trackable.setName(content); } if (localName.equalsIgnoreCase("description")) { trackable.setDetails(content); isMultiline = false; } if (localName.equalsIgnoreCase("esprit_nom")) { if (isInApparition) { logEntryBuilder.setAuthor(content); } else { trackable.setOwner(content); } } if (StringUtils.isNotBlank(content) && localName.equalsIgnoreCase("date_naissance")) { final Date date = DATE_FORMAT.parse(content); trackable.setReleased(date); } if (StringUtils.isNotBlank(content) && localName.equalsIgnoreCase("distance_parcourue")) { trackable.setDistance(Float.parseFloat(content)); } if (localName.equalsIgnoreCase("date_apparition_disparition")) { logEntryBuilder.setDate(DATE_FORMAT.parse(content).getTime()); } if (localName.equalsIgnoreCase("commentaires")) { logEntryBuilder.setLog(content); } if (localName.equalsIgnoreCase("type")) { logEntryBuilder.setLogType(getLogType(content)); } if (localName.equalsIgnoreCase("geolutin")) { trackable.setLogs(logsEntries); // manage spotted field if (!logsEntries.isEmpty()) { // retrieve the first logEntry final LogEntry lastLog = logsEntries.get(0); if (lastLog.getType() == LogType.PLACED_IT) { // it's in a cache trackable.setSpottedType(Trackable.SPOTTED_CACHE); trackable.setSpottedName(lastLog.cacheName); } else if (lastLog.getType() == LogType.RETRIEVED_IT) { trackable.setSpottedName(lastLog.author); // it's in someone hands trackable.setSpottedType(Trackable.SPOTTED_USER); trackable.setSpottedName(lastLog.author); } else { Log.w("GeolutinsHandler.endElement unknown logtype:" + lastLog.getType()); } } } if (localName.equalsIgnoreCase("apparition_disparition")) { isInApparition = false; logsEntries.add(logEntryBuilder.build()); } } catch (final ParseException | NumberFormatException e) { Log.w("Parsing GeoLutins", e); } } @Override public final void characters(final char[] ch, final int start, final int length) throws SAXException { final String text = StringUtils.trim(new String(ch, start, length)); content = isMultiline ? StringUtils.join(content, text) : text; } /** * Convert states from GL to c:geo spotted types. * * @param type * the GL Log type * @return * The LogType */ private static LogType getLogType(final String type) { switch (type) { case "Apparition": // Dropped return LogType.PLACED_IT; case "Disparition": // Retrieved return LogType.RETRIEVED_IT; } return LogType.UNKNOWN; } } @NonNull public static List<Trackable> parse(final InputSource page) { if (page != null) { try { // Create a new instance of the SAX parser final SAXParserFactory saxPF = SAXParserFactory.newInstance(); final SAXParser saxP = saxPF.newSAXParser(); final XMLReader xmlR = saxP.getXMLReader(); // Create the Handler to handle each of the XML tags. final GeolutinsHandler glXMLHandler = new GeolutinsHandler(); xmlR.setContentHandler(glXMLHandler); xmlR.parse(page); return glXMLHandler.getTrackables(); } catch (final SAXException | IOException | ParserConfigurationException e) { Log.w("Cannot parse GeoLutins", e); } } return Collections.emptyList(); } }