aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBananeweizen <bananeweizen@gmx.de>2013-06-22 12:17:08 +0200
committerBananeweizen <bananeweizen@gmx.de>2013-06-22 12:17:08 +0200
commit01bfdcaeeae619fd638f48b8fe94463e8131edd2 (patch)
tree192f5421c231d5e93fa112c21d023a703d9473c5
parent22fa318357e3143ba196871bf5233aed41016766 (diff)
downloadcgeo-01bfdcaeeae619fd638f48b8fe94463e8131edd2.zip
cgeo-01bfdcaeeae619fd638f48b8fe94463e8131edd2.tar.gz
cgeo-01bfdcaeeae619fd638f48b8fe94463e8131edd2.tar.bz2
refactoring: extract GPX serialization to make it testable
-rw-r--r--main/src/cgeo/geocaching/export/GpxExport.java238
-rw-r--r--main/src/cgeo/geocaching/export/GpxSerializer.java256
-rw-r--r--tests/src/cgeo/geocaching/export/GpxSerializerTest.java37
-rw-r--r--tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java17
4 files changed, 323 insertions, 225 deletions
diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java
index 0ba1a3c..e6b52bc 100644
--- a/main/src/cgeo/geocaching/export/GpxExport.java
+++ b/main/src/cgeo/geocaching/export/GpxExport.java
@@ -1,24 +1,12 @@
package cgeo.geocaching.export;
import cgeo.geocaching.Geocache;
-import cgeo.geocaching.LogEntry;
import cgeo.geocaching.R;
import cgeo.geocaching.Settings;
-import cgeo.geocaching.Waypoint;
-import cgeo.geocaching.cgData;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.activity.ActivityMixin;
-import cgeo.geocaching.enumerations.CacheAttribute;
-import cgeo.geocaching.enumerations.LoadFlags;
-import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.utils.AsyncTaskWithProgress;
-import cgeo.geocaching.utils.TextUtils;
import cgeo.geocaching.utils.Log;
-import cgeo.geocaching.utils.XmlUtils;
-import cgeo.org.kxml2.io.KXmlSerializer;
-
-import org.apache.commons.lang3.StringUtils;
-import org.xmlpull.v1.XmlSerializer;
import android.app.Activity;
import android.app.AlertDialog;
@@ -39,22 +27,11 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
-import java.util.Set;
class GpxExport extends AbstractExport {
- private static final SimpleDateFormat dateFormatZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
- public static final String PREFIX_XSI = "http://www.w3.org/2001/XMLSchema-instance";
- public static final String PREFIX_GPX = "http://www.topografix.com/GPX/1/0";
- public static final String PREFIX_GROUNDSPEAK = "http://www.groundspeak.com/cache/1/0";
-
- /**
- * During the export, only this number of geocaches is fully loaded into memory.
- */
- public static final int CACHES_PER_BATCH = 100;
protected GpxExport() {
super(getString(R.string.export_gpx));
@@ -62,7 +39,7 @@ class GpxExport extends AbstractExport {
@Override
public void export(final List<Geocache> caches, final Activity activity) {
- String[] geocodes = getGeocodes(caches);
+ final String[] geocodes = getGeocodes(caches);
if (null == activity) {
// No activity given, so no user interaction possible.
// Start export with default parameters.
@@ -75,10 +52,10 @@ class GpxExport extends AbstractExport {
}
private Dialog getExportDialog(final String[] geocodes, final Activity activity) {
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
// AlertDialog has always dark style, so we have to apply it as well always
- View layout = View.inflate(new ContextThemeWrapper(activity, R.style.dark), R.layout.gpx_export_dialog, null);
+ final View layout = View.inflate(new ContextThemeWrapper(activity, R.style.dark), R.layout.gpx_export_dialog, null);
builder.setView(layout);
final TextView text = (TextView) layout.findViewById(R.id.info);
@@ -108,7 +85,7 @@ class GpxExport extends AbstractExport {
}
private static String[] getGeocodes(final List<Geocache> caches) {
- ArrayList<String> allGeocodes = new ArrayList<String>(caches.size());
+ final ArrayList<String> allGeocodes = new ArrayList<String>(caches.size());
for (final Geocache geocache : caches) {
allGeocodes.add(geocache.getGeocode());
}
@@ -117,7 +94,6 @@ class GpxExport extends AbstractExport {
protected class ExportTask extends AsyncTaskWithProgress<String, File> {
private final Activity activity;
- private int countExported = 0;
/**
* Instantiates and configures the task for exporting field notes.
@@ -137,7 +113,7 @@ class GpxExport extends AbstractExport {
return null;
}
- List<String> allGeocodes = new ArrayList<String>(Arrays.asList(geocodes));
+ final List<String> allGeocodes = new ArrayList<String>(Arrays.asList(geocodes));
setMessage(cgeoapplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, allGeocodes.size(), allGeocodes.size()));
@@ -148,38 +124,21 @@ class GpxExport extends AbstractExport {
final File exportLocation = new File(Settings.getGpxExportDir());
exportLocation.mkdirs();
- final XmlSerializer gpx = new KXmlSerializer();
writer = new BufferedWriter(new FileWriter(exportFile));
- gpx.setOutput(writer);
+ new GpxSerializer().writeGPX(allGeocodes, writer, new GpxSerializer.ProgressListener() {
- gpx.startDocument("UTF-8", true);
- gpx.setPrefix("", PREFIX_GPX);
- gpx.setPrefix("xsi", PREFIX_XSI);
- gpx.setPrefix("groundspeak", PREFIX_GROUNDSPEAK);
- gpx.startTag(PREFIX_GPX, "gpx");
- gpx.attribute("", "version", "1.0");
- gpx.attribute("", "creator", "c:geo - http://www.cgeo.org/");
- gpx.attribute(PREFIX_XSI, "schemaLocation",
- PREFIX_GPX + " http://www.topografix.com/GPX/1/0/gpx.xsd " +
- PREFIX_GROUNDSPEAK + " http://www.groundspeak.com/cache/1/0/1/cache.xsd");
-
- // Split the overall set of geocodes into small chunks. That is a compromise between memory efficiency (because
- // we don't load all caches fully into memory) and speed (because we don't query each cache separately).
- while (!allGeocodes.isEmpty()) {
- final List<String> batch = allGeocodes.subList(0, Math.min(CACHES_PER_BATCH, allGeocodes.size()));
- exportBatch(gpx, batch);
- batch.clear();
- }
-
- gpx.endTag(PREFIX_GPX, "gpx");
- gpx.endDocument();
+ @Override
+ public void publishProgress(int countExported) {
+ this.publishProgress(countExported);
+ }
+ });
} catch (final Exception e) {
Log.e("GpxExport.ExportTask export", e);
if (writer != null) {
try {
writer.close();
- } catch (IOException e1) {
+ } catch (final IOException e1) {
// Ignore double error
}
}
@@ -194,184 +153,13 @@ class GpxExport extends AbstractExport {
return exportFile;
}
- private void exportBatch(final XmlSerializer gpx, Collection<String> geocodesOfBatch) throws IOException {
- Set<Geocache> caches = cgData.loadCaches(geocodesOfBatch, LoadFlags.LOAD_ALL_DB_ONLY);
- for (Geocache cache : caches) {
- gpx.startTag(PREFIX_GPX, "wpt");
- gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude()));
- gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude()));
-
- final Date hiddenDate = cache.getHiddenDate();
- if (hiddenDate != null) {
- XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate));
- }
-
- XmlUtils.multipleTexts(gpx, PREFIX_GPX,
- "name", cache.getGeocode(),
- "desc", cache.getName(),
- "url", cache.getUrl(),
- "urlname", cache.getName(),
- "sym", cache.isFound() ? "Geocache Found" : "Geocache",
- "type", "Geocache|" + cache.getType().pattern);
-
- gpx.startTag(PREFIX_GROUNDSPEAK, "cache");
- gpx.attribute("", "id", cache.getCacheId());
- gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False");
- gpx.attribute("", "archives", cache.isArchived() ? "True" : "False");
-
- XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK,
- "name", cache.getName(),
- "placed_by", cache.getOwnerDisplayName(),
- "owner", cache.getOwnerUserId(),
- "type", cache.getType().pattern,
- "container", cache.getSize().id,
- "difficulty", Float.toString(cache.getDifficulty()),
- "terrain", Float.toString(cache.getTerrain()),
- "country", cache.getLocation(),
- "state", "",
- "encoded_hints", cache.getHint());
-
- writeAttributes(gpx, cache);
-
- gpx.startTag(PREFIX_GROUNDSPEAK, "short_description");
- gpx.attribute("", "html", TextUtils.containsHtml(cache.getShortDescription()) ? "True" : "False");
- gpx.text(cache.getShortDescription());
- gpx.endTag(PREFIX_GROUNDSPEAK, "short_description");
-
- gpx.startTag(PREFIX_GROUNDSPEAK, "long_description");
- gpx.attribute("", "html", TextUtils.containsHtml(cache.getDescription()) ? "True" : "False");
- gpx.text(cache.getDescription());
- gpx.endTag(PREFIX_GROUNDSPEAK, "long_description");
-
- writeLogs(gpx, cache);
-
- gpx.endTag(PREFIX_GROUNDSPEAK, "cache");
- gpx.endTag(PREFIX_GPX, "wpt");
-
- writeWaypoints(gpx, cache);
-
- countExported++;
- publishProgress(countExported);
- }
- }
-
- private void writeWaypoints(final XmlSerializer gpx, final Geocache cache) throws IOException {
- List<Waypoint> waypoints = cache.getWaypoints();
- List<Waypoint> ownWaypoints = new ArrayList<Waypoint>(waypoints.size());
- List<Waypoint> originWaypoints = new ArrayList<Waypoint>(waypoints.size());
- for (Waypoint wp : cache.getWaypoints()) {
- if (wp.isUserDefined()) {
- ownWaypoints.add(wp);
- } else {
- originWaypoints.add(wp);
- }
- }
- int maxPrefix = 0;
- for (Waypoint wp : originWaypoints) {
- String prefix = wp.getPrefix();
- try {
- final int numericPrefix = Integer.parseInt(prefix);
- maxPrefix = Math.max(numericPrefix, maxPrefix);
- } catch (NumberFormatException ex) {
- // ignore non numeric prefix, as it should be unique in the list of non-own waypoints already
- }
- writeCacheWaypoint(gpx, wp, prefix);
- }
- // Prefixes must be unique. There use numeric strings as prefixes in OWN waypoints
- for (Waypoint wp : ownWaypoints) {
- maxPrefix++;
- String prefix = StringUtils.leftPad(String.valueOf(maxPrefix), 2, '0');
- writeCacheWaypoint(gpx, wp, prefix);
- }
- }
-
- /**
- * Writes one waypoint entry for cache waypoint.
- *
- * @param cache
- * The
- * @param wp
- * @param prefix
- * @throws IOException
- */
- private void writeCacheWaypoint(final XmlSerializer gpx, final Waypoint wp, final String prefix) throws IOException {
- final Geopoint coords = wp.getCoords();
- // TODO: create some extension to GPX to include waypoint without coords
- if (coords != null) {
- gpx.startTag(PREFIX_GPX, "wpt");
- gpx.attribute("", "lat", Double.toString(coords.getLatitude()));
- gpx.attribute("", "lon", Double.toString(coords.getLongitude()));
- XmlUtils.multipleTexts(gpx, PREFIX_GPX,
- "name", prefix + wp.getGeocode().substring(2),
- "cmt", wp.getNote(),
- "desc", wp.getName(),
- "sym", wp.getWaypointType().toString(), //TODO: Correct identifier string
- "type", "Waypoint|" + wp.getWaypointType().toString()); //TODO: Correct identifier string
- gpx.endTag(PREFIX_GPX, "wpt");
- }
- }
-
- private void writeLogs(final XmlSerializer gpx, final Geocache cache) throws IOException {
- if (cache.getLogs().isEmpty()) {
- return;
- }
- gpx.startTag(PREFIX_GROUNDSPEAK, "logs");
-
- for (LogEntry log : cache.getLogs()) {
- gpx.startTag(PREFIX_GROUNDSPEAK, "log");
- gpx.attribute("", "id", Integer.toString(log.id));
-
- XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK,
- "date", dateFormatZ.format(new Date(log.date)),
- "type", log.type.type);
-
- gpx.startTag(PREFIX_GROUNDSPEAK, "finder");
- gpx.attribute("", "id", "");
- gpx.text(log.author);
- gpx.endTag(PREFIX_GROUNDSPEAK, "finder");
-
- gpx.startTag(PREFIX_GROUNDSPEAK, "text");
- gpx.attribute("", "encoded", "False");
- gpx.text(log.log);
- gpx.endTag(PREFIX_GROUNDSPEAK, "text");
-
- gpx.endTag(PREFIX_GROUNDSPEAK, "log");
- }
-
- gpx.endTag(PREFIX_GROUNDSPEAK, "logs");
- }
-
- private void writeAttributes(final XmlSerializer gpx, final Geocache cache) throws IOException {
- if (cache.getAttributes().isEmpty()) {
- return;
- }
- //TODO: Attribute conversion required: English verbose name, gpx-id
- gpx.startTag(PREFIX_GROUNDSPEAK, "attributes");
-
- for (String attribute : cache.getAttributes()) {
- final CacheAttribute attr = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attribute));
- if (attr == null) {
- continue;
- }
- final boolean enabled = CacheAttribute.isEnabled(attribute);
-
- gpx.startTag(PREFIX_GROUNDSPEAK, "attribute");
- gpx.attribute("", "id", Integer.toString(attr.gcid));
- gpx.attribute("", "inc", enabled ? "1" : "0");
- gpx.text(attr.getL10n(enabled));
- gpx.endTag(PREFIX_GROUNDSPEAK, "attribute");
- }
-
- gpx.endTag(PREFIX_GROUNDSPEAK, "attributes");
- }
-
@Override
protected void onPostExecuteInternal(final File exportFile) {
if (null != activity) {
if (exportFile != null) {
ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString());
if (Settings.getShareAfterExport()) {
- Intent shareIntent = new Intent();
+ final Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportFile));
shareIntent.setType("application/xml");
diff --git a/main/src/cgeo/geocaching/export/GpxSerializer.java b/main/src/cgeo/geocaching/export/GpxSerializer.java
new file mode 100644
index 0000000..2c50721
--- /dev/null
+++ b/main/src/cgeo/geocaching/export/GpxSerializer.java
@@ -0,0 +1,256 @@
+package cgeo.geocaching.export;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.LogEntry;
+import cgeo.geocaching.Waypoint;
+import cgeo.geocaching.cgData;
+import cgeo.geocaching.enumerations.CacheAttribute;
+import cgeo.geocaching.enumerations.LoadFlags;
+import cgeo.geocaching.geopoint.Geopoint;
+import cgeo.geocaching.utils.TextUtils;
+import cgeo.geocaching.utils.XmlUtils;
+import cgeo.org.kxml2.io.KXmlSerializer;
+
+import org.apache.commons.lang3.StringUtils;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+public final class GpxSerializer {
+
+ private static final SimpleDateFormat dateFormatZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ public static final String PREFIX_XSI = "http://www.w3.org/2001/XMLSchema-instance";
+ public static final String PREFIX_GPX = "http://www.topografix.com/GPX/1/0";
+ public static final String PREFIX_GROUNDSPEAK = "http://www.groundspeak.com/cache/1/0";
+
+ /**
+ * During the export, only this number of geocaches is fully loaded into memory.
+ */
+ public static final int CACHES_PER_BATCH = 100;
+
+ /**
+ * counter for exported caches, used for progress reporting
+ */
+ private int countExported;
+ private ProgressListener progressListener;
+ private XmlSerializer gpx;
+
+ protected static interface ProgressListener {
+
+ void publishProgress(int countExported);
+
+ }
+
+ public void writeGPX(List<String> allGeocodesIn, Writer writer, final ProgressListener progressListener) throws IOException {
+ // create a copy of the geocode list, as we need to modify it, but it might be immutable
+ final ArrayList<String> allGeocodes = new ArrayList<String>(allGeocodesIn);
+
+ this.progressListener = progressListener;
+ gpx = new KXmlSerializer();
+ gpx.setOutput(writer);
+
+ gpx.startDocument("UTF-8", true);
+ gpx.setPrefix("", PREFIX_GPX);
+ gpx.setPrefix("xsi", PREFIX_XSI);
+ gpx.setPrefix("groundspeak", PREFIX_GROUNDSPEAK);
+ gpx.startTag(PREFIX_GPX, "gpx");
+ gpx.attribute("", "version", "1.0");
+ gpx.attribute("", "creator", "c:geo - http://www.cgeo.org/");
+ gpx.attribute(PREFIX_XSI, "schemaLocation",
+ PREFIX_GPX + " http://www.topografix.com/GPX/1/0/gpx.xsd " +
+ PREFIX_GROUNDSPEAK + " http://www.groundspeak.com/cache/1/0/1/cache.xsd");
+
+ // Split the overall set of geocodes into small chunks. That is a compromise between memory efficiency (because
+ // we don't load all caches fully into memory) and speed (because we don't query each cache separately).
+ while (!allGeocodes.isEmpty()) {
+ final List<String> batch = allGeocodes.subList(0, Math.min(CACHES_PER_BATCH, allGeocodes.size()));
+ exportBatch(gpx, batch);
+ batch.clear();
+ }
+
+ gpx.endTag(PREFIX_GPX, "gpx");
+ gpx.endDocument();
+ }
+
+ private void exportBatch(final XmlSerializer gpx, Collection<String> geocodesOfBatch) throws IOException {
+ final Set<Geocache> caches = cgData.loadCaches(geocodesOfBatch, LoadFlags.LOAD_ALL_DB_ONLY);
+ for (final Geocache cache : caches) {
+ gpx.startTag(PREFIX_GPX, "wpt");
+ gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude()));
+ gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude()));
+
+ final Date hiddenDate = cache.getHiddenDate();
+ if (hiddenDate != null) {
+ XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate));
+ }
+
+ XmlUtils.multipleTexts(gpx, PREFIX_GPX,
+ "name", cache.getGeocode(),
+ "desc", cache.getName(),
+ "url", cache.getUrl(),
+ "urlname", cache.getName(),
+ "sym", cache.isFound() ? "Geocache Found" : "Geocache",
+ "type", "Geocache|" + cache.getType().pattern);
+
+ gpx.startTag(PREFIX_GROUNDSPEAK, "cache");
+ gpx.attribute("", "id", cache.getCacheId());
+ gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False");
+ gpx.attribute("", "archives", cache.isArchived() ? "True" : "False");
+
+ XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK,
+ "name", cache.getName(),
+ "placed_by", cache.getOwnerDisplayName(),
+ "owner", cache.getOwnerUserId(),
+ "type", cache.getType().pattern,
+ "container", cache.getSize().id,
+ "difficulty", Float.toString(cache.getDifficulty()),
+ "terrain", Float.toString(cache.getTerrain()),
+ "country", cache.getLocation(),
+ "state", "",
+ "encoded_hints", cache.getHint());
+
+ writeAttributes(cache);
+
+ gpx.startTag(PREFIX_GROUNDSPEAK, "short_description");
+ gpx.attribute("", "html", TextUtils.containsHtml(cache.getShortDescription()) ? "True" : "False");
+ gpx.text(cache.getShortDescription());
+ gpx.endTag(PREFIX_GROUNDSPEAK, "short_description");
+
+ gpx.startTag(PREFIX_GROUNDSPEAK, "long_description");
+ gpx.attribute("", "html", TextUtils.containsHtml(cache.getDescription()) ? "True" : "False");
+ gpx.text(cache.getDescription());
+ gpx.endTag(PREFIX_GROUNDSPEAK, "long_description");
+
+ writeLogs(cache);
+
+ gpx.endTag(PREFIX_GROUNDSPEAK, "cache");
+ gpx.endTag(PREFIX_GPX, "wpt");
+
+ writeWaypoints(cache);
+
+ countExported++;
+ if (progressListener != null) {
+ progressListener.publishProgress(countExported);
+ }
+ }
+ }
+
+ private void writeWaypoints(final Geocache cache) throws IOException {
+ final List<Waypoint> waypoints = cache.getWaypoints();
+ final List<Waypoint> ownWaypoints = new ArrayList<Waypoint>(waypoints.size());
+ final List<Waypoint> originWaypoints = new ArrayList<Waypoint>(waypoints.size());
+ for (final Waypoint wp : cache.getWaypoints()) {
+ if (wp.isUserDefined()) {
+ ownWaypoints.add(wp);
+ } else {
+ originWaypoints.add(wp);
+ }
+ }
+ int maxPrefix = 0;
+ for (final Waypoint wp : originWaypoints) {
+ final String prefix = wp.getPrefix();
+ try {
+ final int numericPrefix = Integer.parseInt(prefix);
+ maxPrefix = Math.max(numericPrefix, maxPrefix);
+ } catch (final NumberFormatException ex) {
+ // ignore non numeric prefix, as it should be unique in the list of non-own waypoints already
+ }
+ writeCacheWaypoint(wp, prefix);
+ }
+ // Prefixes must be unique. There use numeric strings as prefixes in OWN waypoints
+ for (final Waypoint wp : ownWaypoints) {
+ maxPrefix++;
+ final String prefix = StringUtils.leftPad(String.valueOf(maxPrefix), 2, '0');
+ writeCacheWaypoint(wp, prefix);
+ }
+ }
+
+ /**
+ * Writes one waypoint entry for cache waypoint.
+ *
+ * @param cache
+ * The
+ * @param wp
+ * @param prefix
+ * @throws IOException
+ */
+ private void writeCacheWaypoint(final Waypoint wp, final String prefix) throws IOException {
+ final Geopoint coords = wp.getCoords();
+ // TODO: create some extension to GPX to include waypoint without coords
+ if (coords != null) {
+ gpx.startTag(PREFIX_GPX, "wpt");
+ gpx.attribute("", "lat", Double.toString(coords.getLatitude()));
+ gpx.attribute("", "lon", Double.toString(coords.getLongitude()));
+ XmlUtils.multipleTexts(gpx, PREFIX_GPX,
+ "name", prefix + wp.getGeocode().substring(2),
+ "cmt", wp.getNote(),
+ "desc", wp.getName(),
+ "sym", wp.getWaypointType().toString(), //TODO: Correct identifier string
+ "type", "Waypoint|" + wp.getWaypointType().toString()); //TODO: Correct identifier string
+ gpx.endTag(PREFIX_GPX, "wpt");
+ }
+ }
+
+ private void writeLogs(final Geocache cache) throws IOException {
+ if (cache.getLogs().isEmpty()) {
+ return;
+ }
+ gpx.startTag(PREFIX_GROUNDSPEAK, "logs");
+
+ for (final LogEntry log : cache.getLogs()) {
+ gpx.startTag(PREFIX_GROUNDSPEAK, "log");
+ gpx.attribute("", "id", Integer.toString(log.id));
+
+ XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK,
+ "date", dateFormatZ.format(new Date(log.date)),
+ "type", log.type.type);
+
+ gpx.startTag(PREFIX_GROUNDSPEAK, "finder");
+ gpx.attribute("", "id", "");
+ gpx.text(log.author);
+ gpx.endTag(PREFIX_GROUNDSPEAK, "finder");
+
+ gpx.startTag(PREFIX_GROUNDSPEAK, "text");
+ gpx.attribute("", "encoded", "False");
+ gpx.text(log.log);
+ gpx.endTag(PREFIX_GROUNDSPEAK, "text");
+
+ gpx.endTag(PREFIX_GROUNDSPEAK, "log");
+ }
+
+ gpx.endTag(PREFIX_GROUNDSPEAK, "logs");
+ }
+
+ private void writeAttributes(final Geocache cache) throws IOException {
+ if (cache.getAttributes().isEmpty()) {
+ return;
+ }
+ //TODO: Attribute conversion required: English verbose name, gpx-id
+ gpx.startTag(PREFIX_GROUNDSPEAK, "attributes");
+
+ for (final String attribute : cache.getAttributes()) {
+ final CacheAttribute attr = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attribute));
+ if (attr == null) {
+ continue;
+ }
+ final boolean enabled = CacheAttribute.isEnabled(attribute);
+
+ gpx.startTag(PREFIX_GROUNDSPEAK, "attribute");
+ gpx.attribute("", "id", Integer.toString(attr.gcid));
+ gpx.attribute("", "inc", enabled ? "1" : "0");
+ gpx.text(attr.getL10n(enabled));
+ gpx.endTag(PREFIX_GROUNDSPEAK, "attribute");
+ }
+
+ gpx.endTag(PREFIX_GROUNDSPEAK, "attributes");
+ }
+
+}
diff --git a/tests/src/cgeo/geocaching/export/GpxSerializerTest.java b/tests/src/cgeo/geocaching/export/GpxSerializerTest.java
new file mode 100644
index 0000000..0080b76
--- /dev/null
+++ b/tests/src/cgeo/geocaching/export/GpxSerializerTest.java
@@ -0,0 +1,37 @@
+package cgeo.geocaching.export;
+
+import cgeo.geocaching.Geocache;
+import cgeo.geocaching.files.ParserException;
+import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase;
+import cgeo.geocaching.test.R;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class GpxSerializerTest extends AbstractResourceInstrumentationTestCase {
+
+ public static void testWriteEmptyGPX() throws Exception {
+ final StringWriter writer = new StringWriter();
+ new GpxSerializer().writeGPX(Collections.<String> emptyList(), writer, null);
+ assertEquals("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><gpx version=\"1.0\" creator=\"c:geo - http://www.cgeo.org/\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0 http://www.groundspeak.com/cache/1/0/1/cache.xsd\" xmlns=\"http://www.topografix.com/GPX/1/0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:groundspeak=\"http://www.groundspeak.com/cache/1/0\" />", writer.getBuffer().toString());
+ }
+
+ public void testProgressReporting() throws IOException, ParserException {
+ final AtomicReference<Integer> importedCount = new AtomicReference<Integer>(0);
+ final StringWriter writer = new StringWriter();
+
+ Geocache cache = loadCacheFromResource(R.raw.gc1bkp3_gpx101);
+ assertNotNull(cache);
+
+ new GpxSerializer().writeGPX(Collections.singletonList("GC1BKP3"), writer, new GpxSerializer.ProgressListener() {
+
+ @Override
+ public void publishProgress(int countExported) {
+ importedCount.set(countExported);
+ }
+ });
+ assertEquals("Progress listener not called", 1, importedCount.get().intValue());
+ }
+}
diff --git a/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java b/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java
index 304f98d..cb8238f 100644
--- a/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java
+++ b/tests/src/cgeo/geocaching/test/AbstractResourceInstrumentationTestCase.java
@@ -1,11 +1,14 @@
package cgeo.geocaching.test;
+import cgeo.geocaching.Geocache;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.StoredList;
import cgeo.geocaching.cgData;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.LoadFlags;
import cgeo.geocaching.enumerations.LoadFlags.RemoveFlag;
+import cgeo.geocaching.files.GPX10Parser;
+import cgeo.geocaching.files.ParserException;
import android.content.res.Resources;
import android.test.InstrumentationTestCase;
@@ -14,6 +17,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Collection;
import java.util.EnumSet;
import java.util.Scanner;
@@ -78,4 +82,17 @@ public abstract class AbstractResourceInstrumentationTestCase extends Instrument
protected final int getTemporaryListId() {
return temporaryListId;
}
+
+ final protected Geocache loadCacheFromResource(int resourceId) throws IOException, ParserException {
+ final InputStream instream = getResourceStream(resourceId);
+ try {
+ GPX10Parser parser = new GPX10Parser(StoredList.TEMPORARY_LIST_ID);
+ Collection<Geocache> caches = parser.parse(instream, null);
+ assertNotNull(caches);
+ assertFalse(caches.isEmpty());
+ return caches.iterator().next();
+ } finally {
+ instream.close();
+ }
+ }
}