/*
* Copyright (c) 2013, Andreas Billmann <abi@geofroggerfx.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package de.geofroggerfx.gpx;
import de.geofroggerfx.application.ProgressEvent;
import de.geofroggerfx.application.ProgressListener;
import de.geofroggerfx.model.Attribute;
import de.geofroggerfx.model.*;
import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author Andreas Billmann
*/
public class GroundspeakGPXReader implements GPXReader {
public static final String LAT = "lat";
public static final String LON = "lon";
public static final String TIME = "time";
public static final String NAME = "name";
public static final String DESC = "desc";
public static final String WPT = "wpt";
public static final String URL = "url";
public static final String URLNAME = "urlname";
public static final String SYM = "sym";
public static final String TYPE = "type";
public static final String CACHE = "cache";
public static final String GROUNDSPEAK = "groundspeak";
public static final String ID = "id";
public static final String AVAILABLE = "available";
public static final String ARCHIVED = "archived";
public static final String PLACED_BY = "placed_by";
public static final String OWNER = "owner";
public static final String CONTAINER = "container";
public static final String ATTRIBUTES = "attributes";
public static final String ATTRIBUTE = "attribute";
public static final String INC = "inc";
public static final String DIFFICULTY = "difficulty";
public static final String TERRAIN = "terrain";
public static final String COUNTRY = "country";
public static final String STATE = "state";
public static final String SHORT_DESCRIPTION = "short_description";
public static final String HTML = "html";
public static final String LONG_DESCRIPTION = "long_description";
public static final String ENCODED_HINTS = "encoded_hints";
public static final String LOGS = "logs";
public static final String LOG = "log";
public static final String DATE = "date";
public static final String FINDER = "finder";
public static final String ENCODED = "encoded";
public static final String TRAVELBUGS = "travelbugs";
public static final String TRAVELBUG = "travelbug";
public static final String TEXT = "text";
public static final String REF = "ref";
public static final String DEFAULT_NAMESPACE_URL = "http://www.topografix.com/GPX/1/0";
public static final String GROUNDSPEAK_NAMESPACE_URL = "http://www.groundspeak.com/cache/1/0/1";
// 2011-12-03T10:15:30+01:00
private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
private final List<ProgressListener> listeners = new ArrayList<>();
private final Map<Long, User> userCache = new HashMap<>();
private File gpxFile;
private Document content;
private List<Cache> modelList;
private Namespace defaultNamespace;
private Namespace groundspeakNamespace;
@Override
public void addListener(ProgressListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
@Override
public List<Cache> load(final String filename) throws IOException {
checkIfParameterIsNull(filename);
instantiateFileAndAssignToMember(filename);
checkIfFileExists();
readFileContent();
setNamespaces();
parseContent();
return modelList;
}
private void setNamespaces() {
defaultNamespace = Namespace.getNamespace(DEFAULT_NAMESPACE_URL);
groundspeakNamespace = Namespace.getNamespace(GROUNDSPEAK, GROUNDSPEAK_NAMESPACE_URL);
}
private void checkIfParameterIsNull(String filename) {
if (filename == null) {
throw new IllegalArgumentException("filename should not be null");
}
}
private void instantiateFileAndAssignToMember(String filename) {
gpxFile = new File(filename);
}
private void checkIfFileExists() throws FileNotFoundException {
if (!gpxFile.exists()) {
throw new FileNotFoundException("file does not exist");
}
}
private void readFileContent() throws IOException {
fireEvent(new ProgressEvent("GPX Reader",
ProgressEvent.State.STARTED,
"Load File " + gpxFile.getName() + " started."));
countLineNumbers();
final SAXBuilder saxBuilder = new SAXBuilder();
try {
content = saxBuilder.build(gpxFile);
} catch (JDOMException e) {
throw new IOException(e.getMessage(), e);
}
fireEvent(new ProgressEvent("GPX Reader",
ProgressEvent.State.FINISHED,
"Load File " + gpxFile.getName() + " finished."));
}
private void parseContent() {
modelList = new ArrayList<>();
try {
final Element root = content.getRootElement();
final List<Element> waypoints = root.getChildren(WPT, defaultNamespace);
int totalNumberOfCaches = waypoints.size();
int currentNumber = 0;
for (final Element waypointElement : waypoints) {
currentNumber++;
fireEvent(new ProgressEvent("GPX Reader",
ProgressEvent.State.RUNNING,
"Parse " + currentNumber + " of " + totalNumberOfCaches + " caches.",
(double) currentNumber / (double) totalNumberOfCaches));
final Cache cache = parseWaypointElement(waypointElement);
cache.setFound(CacheUtils.hasUserFoundCache(cache, 3906456l));
modelList.add(cache);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private Cache parseWaypointElement(Element waypointElement) {
final Cache cache = new Cache();
final Waypoint mainWaypoint = new Waypoint();
cache.setMainWayPoint(mainWaypoint);
try {
setLatitudeAndLongitude(waypointElement, mainWaypoint);
setTime(waypointElement, mainWaypoint);
setWaypointName(waypointElement, mainWaypoint);
setDescription(waypointElement, mainWaypoint);
setUrl(waypointElement, mainWaypoint);
setUrlName(waypointElement, mainWaypoint);
setSym(waypointElement, mainWaypoint);
setType(waypointElement, mainWaypoint);
final Element cacheElement = waypointElement.getChild(CACHE, groundspeakNamespace);
setId(cacheElement, mainWaypoint);
parseCacheElement(cacheElement, cache);
} catch (DataConversionException | ParseException e) {
// TODO: do some batch error handling
e.printStackTrace();
}
return cache;
}
private void parseCacheElement(Element cacheElement, Cache cache) throws DataConversionException, ParseException {
setId(cacheElement, cache);
setAvailable(cacheElement, cache);
setArchived(cacheElement, cache);
setName(cacheElement, cache);
setPlacedBy(cacheElement, cache);
setOwner(cacheElement, cache);
setType(cacheElement, cache);
setContainer(cacheElement, cache);
setAttributes(cacheElement, cache);
setDifficulty(cacheElement, cache);
setTerrain(cacheElement, cache);
setCountry(cacheElement, cache);
setState(cacheElement, cache);
setShortDescription(cacheElement, cache);
setLongDescription(cacheElement, cache);
setEncodedHints(cacheElement, cache);
setLogs(cacheElement, cache);
setTravelBugs(cacheElement, cache);
}
private void setTravelBugs(Element cacheElement, Cache cache) throws DataConversionException {
final Element travelBugsElement = cacheElement.getChild(TRAVELBUGS, groundspeakNamespace);
if (travelBugsElement != null) {
final List<TravelBug> travelBugs = new ArrayList<>();
cache.setTravelBugs(travelBugs);
for (Element travelBugElement : travelBugsElement.getChildren()) {
final TravelBug travelBug = new TravelBug();
travelBugs.add(travelBug);
setId(travelBugElement, travelBug);
setRef(travelBugElement, travelBug);
setName(travelBugElement, travelBug);
}
}
}
private void setName(Element travelBugElement, TravelBug travelBug) {
travelBug.setName(travelBugElement.getChild(NAME, groundspeakNamespace).getTextTrim());
}
private void setRef(Element travelBugElement, TravelBug travelBug) {
travelBug.setRef(travelBugElement.getAttribute(REF).getValue());
}
private void setId(Element travelBugElement, TravelBug travelBug) throws DataConversionException {
travelBug.setId(travelBugElement.getAttribute(ID).getLongValue());
}
private void setLogs(Element cacheElement, Cache cache) throws DataConversionException, ParseException {
final Element logsElement = cacheElement.getChild(LOGS, groundspeakNamespace);
if (logsElement != null) {
final List<Log> logs = new ArrayList<>();
cache.setLogs(logs);
for (Element logElement : logsElement.getChildren()) {
final Log log = new Log();
logs.add(log);
setId(logElement, log);
setDate(logElement, log);
setType(logElement, log);
setText(logElement, log);
setFinder(logElement, log);
}
}
}
private void setFinder(Element logElement, Log log) throws DataConversionException {
final Element finderElement = logElement.getChild(FINDER, groundspeakNamespace);
if (finderElement != null) {
final Long finderId = finderElement.getAttribute(ID).getLongValue();
User finder = userCache.get(finderId);
if (finder == null) {
finder = new User();
setId(finderElement, finder);
setName(finderElement, finder);
}
log.setFinder(finder);
}
}
private void setText(Element logElement, Log log) throws DataConversionException {
final Element textElement = logElement.getChild(TEXT, groundspeakNamespace);
log.setText(textElement.getTextTrim());
log.setTextEncoded(textElement.getAttribute(ENCODED).getBooleanValue());
}
private void setType(Element logElement, Log log) {
log.setType(logElement.getChild(TYPE, groundspeakNamespace).getTextTrim());
}
private void setId(Element logElement, Log log) throws DataConversionException {
log.setId(logElement.getAttribute(ID).getLongValue());
}
private void setDate(Element logElement, Log log) throws ParseException {
final String dateText = logElement.getChild(DATE, groundspeakNamespace).getTextTrim();
final Date date = DATE_FORMAT.parse(dateText);
log.setDate(date);
}
private void setEncodedHints(Element cacheElement, Cache cache) {
cache.setEncodedHints(cacheElement.getChild(ENCODED_HINTS, groundspeakNamespace).getTextTrim());
}
private void setLongDescription(Element cacheElement, Cache cache) throws DataConversionException {
final Element longDescription = cacheElement.getChild(LONG_DESCRIPTION, groundspeakNamespace);
cache.setLongDescription(longDescription.getTextTrim());
cache.setLongDescriptionHtml(longDescription.getAttribute(HTML).getBooleanValue());
}
private void setShortDescription(Element cacheElement, Cache cache) throws DataConversionException {
final Element shortDescription = cacheElement.getChild(SHORT_DESCRIPTION, groundspeakNamespace);
cache.setShortDescription(shortDescription.getTextTrim());
cache.setShortDescriptionHtml(shortDescription.getAttribute(HTML).getBooleanValue());
}
private void setState(Element cacheElement, Cache cache) {
cache.setState(cacheElement.getChild(STATE, groundspeakNamespace).getTextTrim());
}
private void setCountry(Element cacheElement, Cache cache) {
cache.setCountry(cacheElement.getChild(COUNTRY, groundspeakNamespace).getTextTrim());
}
private void setTerrain(Element cacheElement, Cache cache) {
cache.setTerrain(cacheElement.getChild(TERRAIN, groundspeakNamespace).getTextTrim());
}
private void setDifficulty(Element cacheElement, Cache cache) {
cache.setDifficulty(cacheElement.getChild(DIFFICULTY, groundspeakNamespace).getTextTrim());
}
private void setAttributes(Element cacheElement, Cache cache) throws DataConversionException {
final Element attributesElement = cacheElement.getChild(ATTRIBUTES, groundspeakNamespace);
if (attributesElement != null) {
final List attributes = new ArrayList<>();
cache.setAttributes(attributes);
for (Element attributeElement : attributesElement.getChildren()) {
try {
final Attribute attribute = Attribute.groundspeakAttributeToAttribute(
attributeElement.getAttribute(ID).getLongValue(),
attributeElement.getAttribute(INC).getBooleanValue(),
attributeElement.getTextTrim());
attributes.add(attribute);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void setContainer(Element cacheElement, Cache cache) {
cache.setContainer(cacheElement.getChild(CONTAINER, groundspeakNamespace).getTextTrim());
}
private void setType(Element cacheElement, Cache cache) {
cache.setType(Type.groundspeakStringToType(cacheElement.getChild(TYPE, groundspeakNamespace).getTextTrim()));
}
private void setOwner(Element cacheElement, Cache cache) throws DataConversionException {
final Element ownerElement = cacheElement.getChild(OWNER, groundspeakNamespace);
if (ownerElement != null) {
final Long userId = ownerElement.getAttribute(ID).getLongValue();
User owner = userCache.get(userId);
if (owner == null) {
owner = new User();
setId(ownerElement, owner);
setName(ownerElement, owner);
}
cache.setOwner(owner);
}
}
private void setId(Element userElement, User user) throws DataConversionException {
user.setId(userElement.getAttribute(ID).getLongValue());
}
private void setName(Element userElement, User user) {
user.setName(userElement.getTextTrim());
}
private void setPlacedBy(Element cacheElement, Cache cache) {
cache.setPlacedBy(cacheElement.getChild(PLACED_BY, groundspeakNamespace).getTextTrim());
}
private void setArchived(Element cacheElement, Cache cache) throws DataConversionException {
cache.setArchived(cacheElement.getAttribute(ARCHIVED).getBooleanValue());
}
private void setAvailable(Element cacheElement, Cache cache) throws DataConversionException {
cache.setAvailable(cacheElement.getAttribute(AVAILABLE).getBooleanValue());
}
private void setId(Element cacheElement, Cache cache) throws DataConversionException {
cache.setId(cacheElement.getAttribute(ID).getLongValue());
}
private void setType(Element cacheElement, Waypoint waypoint) {
waypoint.setType(cacheElement.getChild(TYPE, defaultNamespace).getTextTrim());
}
private void setSym(Element cacheElement, Waypoint waypoint) {
waypoint.setSymbol(cacheElement.getChild(SYM, defaultNamespace).getTextTrim());
}
private void setUrlName(Element cacheElement, Waypoint waypoint) {
waypoint.setUrlName(cacheElement.getChild(URLNAME, defaultNamespace).getTextTrim());
}
private void setUrl(Element cacheElement, Waypoint waypoint) {
waypoint.setUrl(cacheElement.getChild(URL, defaultNamespace).getTextTrim());
}
private void setDescription(Element cacheElement, Waypoint waypoint) {
waypoint.setDescription(cacheElement.getChild(DESC, defaultNamespace).getTextTrim());
}
private void setWaypointName(Element cacheElement, Waypoint waypoint) {
waypoint.setName(cacheElement.getChild(NAME, defaultNamespace).getTextTrim());
}
private void setLatitudeAndLongitude(Element cacheElement, Waypoint waypoint) throws DataConversionException {
waypoint.setLatitude(cacheElement.getAttribute(LAT).getDoubleValue());
waypoint.setLongitude(cacheElement.getAttribute(LON).getDoubleValue());
}
private void setTime(Element cacheElement, Waypoint waypoint) throws ParseException {
final String timeText = cacheElement.getChild(TIME, defaultNamespace).getTextTrim();
final Date date = DATE_FORMAT.parse(timeText);
waypoint.setTime(date);
}
private void setName(Element cacheElement, Cache cache) {
cache.setName(cacheElement.getChild(NAME, groundspeakNamespace).getTextTrim());
}
private void setId(Element cacheElement, Waypoint waypoint) throws DataConversionException {
waypoint.setId(cacheElement.getAttribute(ID).getLongValue());
}
private void fireEvent(ProgressEvent event) {
listeners.stream().forEach((l) -> {
l.progress(event);
});
}
private void countLineNumbers() {
int totalNumberOfLines = 0;
Charset charset = Charset.forName("UTF-8");
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = Files.newBufferedReader(gpxFile.toPath(), charset)) {
String line = null;
while ((line = reader.readLine()) != null) {
totalNumberOfLines++;
}
fireEvent(new ProgressEvent("GPX Reader",
ProgressEvent.State.RUNNING,
totalNumberOfLines + " lines to read!"));
} catch (IOException x) {
x.printStackTrace();
}
}
}