Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java	(revision 15496)
@@ -36,4 +36,5 @@
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
 
 /**
@@ -64,5 +65,5 @@
     @Test
     public void testMergeFrom() {
-        ImmutableGpxTrack track = singleWaypointGpxTrack();
+        GpxTrack track = singleWaypointGpxTrack();
         GpxRoute route = singleWaypointRoute();
         WayPoint newWP = new WayPoint(LatLon.NORTH_POLE);
@@ -103,5 +104,7 @@
         final GpxData expected = getGpx(exp);
         own.mergeFrom(other, cut, connect);
-        assertEquals(expected, own);
+        own.put(GpxConstants.META_BOUNDS, null);
+        expected.put(GpxConstants.META_BOUNDS, null); //they are only updated by GpxWriter
+        assertEquals(exp + " didn't match!", expected, own);
     }
 
@@ -117,6 +120,6 @@
         assertEquals(0, data.getTracks().size());
 
-        ImmutableGpxTrack track1 = emptyGpxTrack();
-        ImmutableGpxTrack track2 = singleWaypointGpxTrack();
+        GpxTrack track1 = emptyGpxTrack();
+        GpxTrack track2 = singleWaypointGpxTrack();
         data.addTrack(track1);
         assertEquals(1, data.getTracks().size());
@@ -137,5 +140,5 @@
     @Test(expected = IllegalArgumentException.class)
     public void testAddTrackFails() {
-        ImmutableGpxTrack track1 = emptyGpxTrack();
+        GpxTrack track1 = emptyGpxTrack();
         data.addTrack(track1);
         data.addTrack(track1);
@@ -147,5 +150,5 @@
     @Test(expected = IllegalArgumentException.class)
     public void testRemoveTrackFails() {
-        ImmutableGpxTrack track1 = emptyGpxTrack();
+        GpxTrack track1 = emptyGpxTrack();
         data.addTrack(track1);
         data.removeTrack(track1);
@@ -246,8 +249,8 @@
     public void testHasTrackPoints() {
         assertFalse(data.hasTrackPoints());
-        ImmutableGpxTrack track1 = emptyGpxTrack();
+        GpxTrack track1 = emptyGpxTrack();
         data.addTrack(track1);
         assertFalse(data.hasTrackPoints());
-        ImmutableGpxTrack track2 = singleWaypointGpxTrack();
+        GpxTrack track2 = singleWaypointGpxTrack();
         data.addTrack(track2);
         assertTrue(data.hasTrackPoints());
@@ -260,8 +263,8 @@
     public void testGetTrackPoints() {
         assertEquals(0, data.getTrackPoints().count());
-        ImmutableGpxTrack track1 = singleWaypointGpxTrack();
+        GpxTrack track1 = singleWaypointGpxTrack();
         data.addTrack(track1);
         assertEquals(1, data.getTrackPoints().count());
-        ImmutableGpxTrack track2 = singleWaypointGpxTrack();
+        GpxTrack track2 = singleWaypointGpxTrack();
         data.addTrack(track2);
         assertEquals(2, data.getTrackPoints().count());
@@ -281,5 +284,5 @@
     @Test
     public void testIsEmpty() {
-        ImmutableGpxTrack track1 = singleWaypointGpxTrack();
+        GpxTrack track1 = singleWaypointGpxTrack();
         WayPoint waypoint = new WayPoint(LatLon.ZERO);
         GpxRoute route = singleWaypointRoute();
@@ -308,9 +311,9 @@
     @Test
     public void testLength() {
-        ImmutableGpxTrack track1 = waypointGpxTrack(
+        GpxTrack track1 = waypointGpxTrack(
                 new WayPoint(new LatLon(0, 0)),
                 new WayPoint(new LatLon(1, 1)),
                 new WayPoint(new LatLon(0, 2)));
-        ImmutableGpxTrack track2 = waypointGpxTrack(
+        GpxTrack track2 = waypointGpxTrack(
                 new WayPoint(new LatLon(0, 0)),
                 new WayPoint(new LatLon(-1, 1)));
@@ -336,6 +339,6 @@
         p2.setTime(new Date(100020));
         p4.setTime(new Date(500020));
-        data.addTrack(new ImmutableGpxTrack(Arrays.asList(Arrays.asList(p1, p2)), Collections.emptyMap()));
-        data.addTrack(new ImmutableGpxTrack(Arrays.asList(Arrays.asList(p3, p4, p5)), Collections.emptyMap()));
+        data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p1, p2)), Collections.emptyMap()));
+        data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p3, p4, p5)), Collections.emptyMap()));
 
         Date[] times = data.getMinMaxTimeForAllTracks();
@@ -355,5 +358,5 @@
                 .map(WayPoint::new)
                 .collect(Collectors.toList());
-        data.addTrack(new ImmutableGpxTrack(Arrays.asList(points), Collections.emptyMap()));
+        data.addTrack(new GpxTrack(Arrays.asList(points), Collections.emptyMap()));
 
         WayPoint closeToMiddle = data.nearestPointOnTrack(new EastNorth(10, 0), 10);
@@ -449,14 +452,14 @@
     }
 
-    private static ImmutableGpxTrack emptyGpxTrack() {
-        return new ImmutableGpxTrack(Collections.<Collection<WayPoint>>emptyList(), Collections.emptyMap());
-    }
-
-    private static ImmutableGpxTrack singleWaypointGpxTrack() {
-        return new ImmutableGpxTrack(Collections.singleton(Collections.singleton(new WayPoint(LatLon.ZERO))), Collections.emptyMap());
-    }
-
-    private static ImmutableGpxTrack waypointGpxTrack(WayPoint... wps) {
-        return new ImmutableGpxTrack(Collections.singleton(Arrays.asList(wps)), Collections.emptyMap());
+    private static GpxTrack emptyGpxTrack() {
+        return new GpxTrack(Collections.<Collection<WayPoint>>emptyList(), Collections.emptyMap());
+    }
+
+    private static GpxTrack singleWaypointGpxTrack() {
+        return new GpxTrack(Collections.singleton(Collections.singleton(new WayPoint(LatLon.ZERO))), Collections.emptyMap());
+    }
+
+    private static GpxTrack waypointGpxTrack(WayPoint... wps) {
+        return new GpxTrack(Collections.singleton(Arrays.asList(wps)), Collections.emptyMap());
     }
 
@@ -473,8 +476,12 @@
     public void testEqualsContract() {
         TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
         EqualsVerifier.forClass(GpxData.class).usingGetClass()
-            .withIgnoredFields("attr", "creator", "fromServer", "storageFile", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans")
+            .suppress(Warning.NONFINAL_FIELDS)
+            .withIgnoredFields("creator", "fromServer", "storageFile", "initializing", "updating", "suppressedInvalidate", "listeners", "tracks", "routes", "waypoints", "proxy", "segSpans", "modified")
             .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
             .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create())
+            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
             .verify();
     }
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxExtensionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxExtensionTest.java	(revision 15496)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxExtensionTest.java	(revision 15496)
@@ -0,0 +1,46 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.gui.layer.gpx.ConvertToDataLayerActionTest;
+import org.openstreetmap.josm.io.GpxReaderTest;
+import org.openstreetmap.josm.io.GpxWriterTest;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+/**
+ * Unit tests for class {@link GpxExtension}
+ */
+public class GpxExtensionTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Unit test of methods {@link GpxExtension#equals} and {@link GpxExtension#hashCode}.
+     * @see GpxWriterTest#testExtensions()
+     * @see GpxReaderTest#testLayerPrefs()
+     * @see ConvertToDataLayerActionTest#testFromTrack()
+     */
+    @Test
+    public void testEqualsContract() {
+        TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
+        EqualsVerifier.forClass(GpxExtension.class).usingGetClass()
+        .suppress(Warning.NONFINAL_FIELDS)
+        .withIgnoredFields("qualifiedName")
+        .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
+        .verify();
+    }
+
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxRouteTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxRouteTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxRouteTest.java	(revision 15496)
@@ -30,7 +30,10 @@
     public void testEqualsContract() {
         TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
         EqualsVerifier.forClass(GpxRoute.class).usingGetClass()
             .suppress(Warning.NONFINAL_FIELDS)
             .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
+            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
             .verify();
     }
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackSegmentTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackSegmentTest.java	(revision 15496)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackSegmentTest.java	(revision 15496)
@@ -0,0 +1,41 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+/**
+ * Unit tests for class {@link GpxTrackSegment}.
+ */
+public class GpxTrackSegmentTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Unit test of methods {@link GpxTrackSegment#equals} and {@link GpxTrackSegment#hashCode}.
+     */
+    @Test
+    public void testEqualsContract() {
+        TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
+        EqualsVerifier.forClass(GpxTrackSegment.class).usingGetClass()
+            .suppress(Warning.NONFINAL_FIELDS)
+            .withIgnoredFields("bounds", "length")
+            .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
+            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
+            .verify();
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java	(revision 15496)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxTrackTest.java	(revision 15496)
@@ -0,0 +1,74 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.tools.ListenerList;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import nl.jqno.equalsverifier.EqualsVerifier;
+import nl.jqno.equalsverifier.Warning;
+
+/**
+ * Unit tests for class {@link GpxTrack}.
+ */
+public class GpxTrackTest {
+
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Tests weather the track can read and write colors.
+     */
+    @Test
+    public void testColors() {
+        GpxTrack trk = new GpxTrack(new ArrayList<IGpxTrackSegment>(), new HashMap<>());
+        GpxExtensionCollection ext = trk.getExtensions();
+        ext.add("gpxd", "color", "#FF0000");
+        trk.invalidate();
+        assertEquals(trk.getColor(), Color.RED);
+        trk.setColor(Color.GREEN);
+        assertEquals(trk.getColor(), Color.GREEN);
+        trk.invalidate();
+        assertEquals(trk.getColor(), Color.GREEN);
+        ext.remove("gpxd", "color");
+        trk.invalidate();
+        assertNull(trk.getColor());
+        ext.add("gpxx", "TrackExtensions").getExtensions().add("gpxx", "DisplayColor", "Blue");
+        trk.invalidate();
+        assertEquals(trk.getColor(), Color.BLUE);
+        trk.setColor(null);
+        assertNull(trk.getColor());
+        trk.invalidate();
+        assertNull(trk.getColor());
+    }
+
+    /**
+     * Unit test of methods {@link GpxTrack#equals} and {@link GpxTrack#hashCode}.
+     */
+    @Test
+    public void testEqualsContract() {
+        TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
+        EqualsVerifier.forClass(GpxTrack.class).usingGetClass()
+            .suppress(Warning.NONFINAL_FIELDS)
+            .withPrefabValues(ListenerList.class, ListenerList.create(), ListenerList.create())
+            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
+            .withIgnoredFields("bounds", "length", "colorCache", "colorFormat", "listeners")
+            .verify();
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegmentTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegmentTest.java	(revision 15487)
+++ 	(revision )
@@ -1,36 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.gpx;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-/**
- * Unit tests for class {@link ImmutableGpxTrackSegment}.
- */
-public class ImmutableGpxTrackSegmentTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
-     * Unit test of methods {@link ImmutableGpxTrackSegment#equals} and {@link ImmutableGpxTrackSegment#hashCode}.
-     */
-    @Test
-    public void testEqualsContract() {
-        TestUtils.assumeWorkingEqualsVerifier();
-        EqualsVerifier.forClass(ImmutableGpxTrackSegment.class).usingGetClass()
-            .withIgnoredFields("bounds", "length")
-            .withPrefabValues(WayPoint.class, new WayPoint(LatLon.NORTH_POLE), new WayPoint(LatLon.SOUTH_POLE))
-            .verify();
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackTest.java	(revision 15487)
+++ 	(revision )
@@ -1,36 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data.gpx;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import nl.jqno.equalsverifier.EqualsVerifier;
-import nl.jqno.equalsverifier.Warning;
-
-/**
- * Unit tests for class {@link ImmutableGpxTrack}.
- */
-public class ImmutableGpxTrackTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
-     * Unit test of methods {@link ImmutableGpxTrack#equals} and {@link ImmutableGpxTrack#hashCode}.
-     */
-    @Test
-    public void testEqualsContract() {
-        TestUtils.assumeWorkingEqualsVerifier();
-        EqualsVerifier.forClass(ImmutableGpxTrack.class).usingGetClass()
-            .suppress(Warning.NONFINAL_FIELDS)
-            .withIgnoredFields("bounds", "length")
-            .verify();
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/WayPointTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/WayPointTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/WayPointTest.java	(revision 15496)
@@ -29,7 +29,10 @@
     public void testEqualsContract() {
         TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
         EqualsVerifier.forClass(WayPoint.class).usingGetClass()
             .suppress(Warning.NONFINAL_FIELDS)
             .withIgnoredFields("customColoring", "dir", "drawLine", "east", "north", "eastNorthCacheKey")
+            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
             .verify();
     }
Index: trunk/test/unit/org/openstreetmap/josm/data/gpx/WithAttributesTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/gpx/WithAttributesTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/data/gpx/WithAttributesTest.java	(revision 15496)
@@ -29,6 +29,9 @@
     public void testEqualsContract() {
         TestUtils.assumeWorkingEqualsVerifier();
+        GpxExtensionCollection col = new GpxExtensionCollection();
+        col.add("josm", "from-server", "true");
         EqualsVerifier.forClass(WithAttributes.class).usingGetClass()
             .suppress(Warning.NONFINAL_FIELDS)
+            .withPrefabValues(GpxExtensionCollection.class, new GpxExtensionCollection(), col)
             .verify();
     }
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java	(revision 15496)
@@ -4,4 +4,5 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -19,5 +20,6 @@
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -74,13 +76,17 @@
     public void testGpxLayer() throws Exception {
         GpxLayer layer = new GpxLayer(new GpxData(), "foo", false);
+        GpxTrack trk = new GpxTrack(new ArrayList<IGpxTrackSegment>(), new HashMap<>());
+        trk.getExtensions().add("gpxd", "color", "#FF0000");
+        layer.data.addTrack(trk);
+
         assertEquals("foo", layer.getName());
         assertFalse(layer.isLocalFile());
-        assertEquals(Color.MAGENTA, layer.getColorProperty().get());
-        assertEquals("<html>0 tracks (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText());
+        assertEquals(layer.getColor(), Color.RED);
+        assertEquals("<html>1 track (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText());
 
         GpxLayer layer2 = new GpxLayer(new GpxData(), "bar", true);
         assertEquals("bar", layer2.getName());
         assertTrue(layer2.isLocalFile());
-        assertEquals(Color.MAGENTA, layer2.getColorProperty().get());
+        assertNull(layer2.getColor());
         assertEquals("<html>0 tracks (0 segments), 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer2.getToolTipText());
 
@@ -188,5 +194,5 @@
     public void testGetTimespanForTrack() throws Exception {
         assertEquals("", GpxLayer.getTimespanForTrack(
-                new ImmutableGpxTrack(new ArrayList<Collection<WayPoint>>(), new HashMap<String, Object>())));
+                new GpxTrack(new ArrayList<Collection<WayPoint>>(), new HashMap<String, Object>())));
 
         assertEquals("1/3/16 11:59 AM - 12:00 PM (0:00)", GpxLayer.getTimespanForTrack(getMinimalGpxData().tracks.iterator().next()));
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java	(revision 15496)
@@ -8,5 +8,4 @@
 import static org.junit.Assert.assertTrue;
 
-import java.awt.Color;
 import java.io.File;
 
@@ -14,6 +13,4 @@
 import org.junit.Rule;
 import org.junit.Test;
-import org.openstreetmap.josm.data.preferences.AbstractProperty;
-import org.openstreetmap.josm.data.preferences.NamedColorProperty;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
@@ -41,23 +38,4 @@
     public void setUp() {
         testLayer = new LayerManagerTest.TestLayer();
-    }
-
-    /**
-     * Test {@link Layer#getColorProperty()}
-     */
-    @Test
-    public void testGetColorProperty() {
-        assertEquals(null, testLayer.getColorProperty());
-
-        AbstractProperty<Color> color = new LayerManagerTest.TestLayer() {
-            @Override
-            protected NamedColorProperty getBaseColorProperty() {
-                return new NamedColorProperty("x", Color.BLACK);
-            }
-        }.getColorProperty();
-
-        assertEquals(Color.BLACK, color.get());
-        assertEquals(Color.BLACK, color.getDefaultValue());
-        assertEquals("clr.layer.Test Layer.x", color.getKey());
     }
 
@@ -98,10 +76,5 @@
         assertEquals("Test Layer2", testLayer.getName());
 
-        testLayer = new LayerManagerTest.TestLayer() {
-            @Override
-            public AbstractProperty<Color> getColorProperty() {
-                return new NamedColorProperty("test", Color.RED);
-            }
-        };
+        testLayer = new LayerManagerTest.TestLayer();
 
         testLayer.setName("Test Layer2");
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/MarkerLayerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/MarkerLayerTest.java	(revision 15487)
+++ 	(revision )
@@ -1,49 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.layer;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.gui.MainApplication;
-import org.openstreetmap.josm.gui.MapFrame;
-import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
-/**
- * Unit tests of {@link MarkerLayer} class.
- */
-public class MarkerLayerTest {
-
-    /**
-     * For creating layers
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection();
-
-    /**
-     * Unit test of {@code Main.map.mapView.playHeadMarker}.
-     */
-    @Test
-    public void testPlayHeadMarker() {
-        try {
-            MainApplication.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "", null));
-            MapFrame map = MainApplication.getMap();
-            MarkerLayer layer = new MarkerLayer(new GpxData(), null, null, null);
-            assertNull(map.mapView.playHeadMarker);
-            MainApplication.getLayerManager().addLayer(layer);
-            assertNotNull(map.mapView.playHeadMarker);
-            MainApplication.getLayerManager().removeLayer(layer);
-        } finally {
-            if (MainApplication.isDisplayingMapView()) {
-                MainApplication.getMap().mapView.playHeadMarker = null;
-            }
-        }
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java	(revision 15496)
@@ -24,5 +24,5 @@
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
-import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
+import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -241,5 +241,5 @@
         assertEquals(1, gpx.getTrackCount());
         GpxTrack track = gpx.getTracks().iterator().next();
-        Collection<GpxTrackSegment> segments = track.getSegments();
+        Collection<IGpxTrackSegment> segments = track.getSegments();
         assertEquals(1, segments.size());
         Collection<WayPoint> trackpoints = segments.iterator().next().getWayPoints();
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityActionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityActionTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityActionTest.java	(revision 15496)
@@ -39,13 +39,13 @@
             @Override
             protected String getString(final ExtendedDialog instance) {
-                return ((JLabel) ((JPanel) this.getContent(instance)).getComponent(2)).getText();
+                return ((JLabel) ((JPanel) instance.getContentPane().getComponent(0)).getComponent(2)).getText();
             }
         };
         edMocker.getMockResultMap().put(
-            "<html>Select all tracks that you want to be displayed. You can drag select a range of " +
-            "tracks or use CTRL+Click to select specific ones. The map is updated live in the " +
-            "background. Open the URLs by double clicking them.</html>",
-            "Show all"
-        );
+                "<html>Select all tracks that you want to be displayed. " +
+                        "You can drag select a range of tracks or use CTRL+Click to select specific ones. " +
+                        "The map is updated live in the background. Open the URLs by double clicking them, edit name and description by double clicking the cell.</html>",
+                        "Show all"
+                );
 
         new ChooseTrackVisibilityAction(GpxLayerTest.getMinimalGpxLayer()).actionPerformed(null);
@@ -53,5 +53,5 @@
         assertEquals(1, edMocker.getInvocationLog().size());
         Object[] invocationLogEntry = edMocker.getInvocationLog().get(0);
-        assertEquals(1, (int) invocationLogEntry[0]);
+        assertEquals(2, (int) invocationLogEntry[0]);
         assertEquals("Set track visibility for Bananas", invocationLogEntry[2]);
     }
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerActionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerActionTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerActionTest.java	(revision 15496)
@@ -67,18 +67,31 @@
     public void testFromTrack() throws Exception {
         Config.getPref().put("gpx.convert-tags", "no");
-        testFromTrack("tracks");
+        testFromTrack("tracks.gpx", "tracks.osm");
 
         Config.getPref().put("gpx.convert-tags", "yes");
-        testFromTrack("tracks-ele-time");
+        testFromTrack("tracks.gpx", "tracks-ele-time.osm");
 
         Config.getPref().put("gpx.convert-tags", "list");
         Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("ele"));
         Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList("time"));
-        testFromTrack("tracks-ele");
-
+        testFromTrack("tracks.gpx", "tracks-ele.osm");
 
         Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("time"));
         Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList("ele"));
-        testFromTrack("tracks-time");
+        testFromTrack("tracks.gpx", "tracks-time.osm");
+
+        //Extension tests:
+        Config.getPref().put("gpx.convert-tags", "yes");
+        testFromTrack("tracks-extensions.gpx", "tracks-extensions.osm");
+
+        Config.getPref().put("gpx.convert-tags", "list");
+        Config.getPref().putList("gpx.convert-tags.list.yes", Arrays.asList("time", "ele"));
+        Config.getPref().putList("gpx.convert-tags.list.no", Arrays.asList(
+                "gpxx:DisplayColor",
+                "gpxd:color",
+                "gpx:extension:test:tag",
+                "gpx:extension:test:segment:tag"));
+        testFromTrack("tracks-extensions.gpx", "tracks-ele-time.osm");
+
     }
 
@@ -107,11 +120,11 @@
     }
 
-    private void testFromTrack(String expected) throws IOException, SAXException, IllegalDataException {
-        final GpxData data = GpxReaderTest.parseGpxData(TestUtils.getTestDataRoot() + "tracks/tracks.gpx");
+    private void testFromTrack(String originalGpx, String expectedOsm) throws IOException, SAXException, IllegalDataException {
+        final GpxData data = GpxReaderTest.parseGpxData(TestUtils.getTestDataRoot() + "tracks/" + originalGpx);
         final DataSet osmExpected = OsmReader.parseDataSet(Files.newInputStream(
-                Paths.get(TestUtils.getTestDataRoot(), "tracks/" + expected + ".osm")), null);
+                Paths.get(TestUtils.getTestDataRoot(), "tracks/" + expectedOsm)), null);
         final GpxLayer layer = new GpxLayer(data);
         final DataSet osm = new ConvertFromGpxLayerAction(layer).convert();
-        //compare sorted coordinates/tags and total amount of primitives, because IDs and order will vary after reload
+        //compare sorted nodes/ways, tags and total amount of primitives, because IDs and order will vary after reload
 
         List<GenericNode> nodes = osm.getNodes().stream()
@@ -125,6 +138,19 @@
                 .collect(Collectors.toList());
 
-        assertEquals(nodesExpected, nodes);
-        assertEquals(osmExpected.allPrimitives().size(), osm.allPrimitives().size());
+        assertEquals("Conversion " + originalGpx + " -> " + expectedOsm + " didn't match!", nodesExpected, nodes);
+
+        List<String> ways = osm.getWays().stream()
+                .map(w -> Integer.toString(w.getNodes().size()) + ":" + w.getKeys().entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey())).collect(Collectors.toList()).toString())
+                .sorted()
+                .collect(Collectors.toList());
+
+        List<String> waysExpected = osmExpected.getWays().stream()
+                .map(w -> Integer.toString(w.getNodes().size()) + ":" + w.getKeys().entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey())).collect(Collectors.toList()).toString())
+                .sorted()
+                .collect(Collectors.toList());
+
+        assertEquals("Conversion " + originalGpx + " -> " + expectedOsm + " didn't match!", waysExpected, ways);
+
+        assertEquals("Conversion " + originalGpx + " -> " + expectedOsm + " didn't match!", osmExpected.allPrimitives().size(), osm.allPrimitives().size());
     }
 
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java	(revision 15496)
@@ -7,4 +7,5 @@
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
@@ -14,9 +15,11 @@
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper.ColorMode;
 import org.openstreetmap.josm.io.GpxReaderTest;
-import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.ColorHelper;
 import org.xml.sax.SAXException;
+
+import com.google.common.collect.ImmutableMap;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -42,8 +45,9 @@
     @Test
     public void testTicket12312() throws FileNotFoundException, IOException, SAXException {
-        Config.getPref().putBoolean("draw.rawgps.colors.dynamic.layer 12312", true);
-        Config.getPref().putInt("draw.rawgps.colors.layer 12312", GpxDrawHelper.ColorMode.VELOCITY.toIndex());
-        final List<String> colors = calculateColors(TestUtils.getRegressionDataFile(12312, "single_trackpoint.gpx"), "12312", 1);
-        assertEquals("[#FF00FF]", colors.toString());
+        final List<String> colors = calculateColors(TestUtils.getRegressionDataFile(12312, "single_trackpoint.gpx"),
+                ImmutableMap.of("colormode.dynamic-range", "true",
+                        "colormode", Integer.toString(ColorMode.VELOCITY.toIndex())),
+                1);
+        assertEquals("[null]", colors.toString());
     }
 
@@ -56,6 +60,6 @@
     @Test
     public void testNone() throws IOException, SAXException {
-        final List<String> colors = calculateColors("data_nodist/2094047.gpx", "000", 10);
-        assertEquals("[#FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF, #FF00FF]", colors.toString());
+        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of(), 10);
+        assertEquals("[#000000, #000000, #000000, #000000, #000000, #000000, #000000, #000000, #000000, #000000]", colors.toString());
     }
 
@@ -68,7 +72,6 @@
     @Test
     public void testVelocity() throws IOException, SAXException {
-        Config.getPref().putInt("draw.rawgps.colors.layer 001", GpxDrawHelper.ColorMode.VELOCITY.toIndex());
-        final List<String> colors = calculateColors("data_nodist/2094047.gpx", "001", 10);
-        assertEquals("[#FF00FF, #FFAD00, #FFA800, #FFA800, #FF9E00, #FF9400, #FF7000, #FF7000, #FF8000, #FF9400]", colors.toString());
+        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of("colormode", Integer.toString(ColorMode.VELOCITY.toIndex())), 10);
+        assertEquals("[#000000, #FFAD00, #FFA800, #FFA800, #FF9E00, #FF9400, #FF7000, #FF7000, #FF8000, #FF9400]", colors.toString());
     }
 
@@ -81,8 +84,9 @@
     @Test
     public void testVelocityDynamic() throws IOException, SAXException {
-        Config.getPref().putInt("draw.rawgps.colors.layer 002", GpxDrawHelper.ColorMode.VELOCITY.toIndex());
-        Config.getPref().putBoolean("draw.rawgps.colors.dynamic.layer 002", true);
-        final List<String> colors = calculateColors("data_nodist/2094047.gpx", "002", 10);
-        assertEquals("[#FF00FF, #00FFE0, #00FFC2, #00FFC2, #00FF75, #00FF3D, #99FF00, #94FF00, #38FF00, #00FF38]", colors.toString());
+        final List<String> colors = calculateColors("data_nodist/2094047.gpx",
+                ImmutableMap.of("colormode.dynamic-range", "true",
+                        "colormode", Integer.toString(ColorMode.VELOCITY.toIndex())),
+                10);
+        assertEquals("[#000000, #00FFE0, #00FFC2, #00FFC2, #00FF75, #00FF3D, #99FF00, #94FF00, #38FF00, #00FF38]", colors.toString());
     }
 
@@ -95,7 +99,6 @@
     @Test
     public void testDirection() throws IOException, SAXException {
-        Config.getPref().putInt("draw.rawgps.colors.layer 003", GpxDrawHelper.ColorMode.DIRECTION.toIndex());
-        final List<String> colors = calculateColors("data_nodist/2094047.gpx", "003", 10);
-        assertEquals("[#FF00FF, #EAEC25, #EDEA26, #EDE525, #ECD322, #EBB81D, #E85A0D, #E73708, #E84D0B, #EA8A15]", colors.toString());
+        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of("colormode", Integer.toString(ColorMode.DIRECTION.toIndex())), 10);
+        assertEquals("[#000000, #EAEC25, #EDEA26, #EDE525, #ECD322, #EBB81D, #E85A0D, #E73708, #E84D0B, #EA8A15]", colors.toString());
     }
 
@@ -108,7 +111,6 @@
     @Test
     public void testTime() throws IOException, SAXException {
-        Config.getPref().putInt("draw.rawgps.colors.layer 003", GpxDrawHelper.ColorMode.TIME.toIndex());
-        final List<String> colors = calculateColors("data_nodist/2094047.gpx", "003", 10);
-        assertEquals("[#FF00FF, #FF0000, #FF0000, #FF0500, #FF0500, #FF0A00, #FF0A00, #FF1F00, #FF2E00, #FF3300]", colors.toString());
+        final List<String> colors = calculateColors("data_nodist/2094047.gpx", ImmutableMap.of("colormode", Integer.toString(ColorMode.TIME.toIndex())), 10);
+        assertEquals("[#000000, #FF0000, #FF0000, #FF0500, #FF0500, #FF0A00, #FF0A00, #FF1F00, #FF2E00, #FF3300]", colors.toString());
     }
 
@@ -116,5 +118,5 @@
      *
      * @param fileName the GPX filename to parse
-     * @param layerName the layer name used to fetch the color settings, see {@link GpxDrawHelper#readPreferences(java.lang.String)}
+     * @param layerPrefs a HashMap representing the layer specific preferences
      * @param n the number of waypoints of the first track/segment to analyze
      * @return the HTML color codes for the first {@code n} points
@@ -123,9 +125,10 @@
      * @throws SAXException if any SAX error occurs
      */
-    static List<String> calculateColors(String fileName, String layerName, int n) throws IOException, SAXException {
+    static List<String> calculateColors(String fileName, Map<String, String> layerPrefs, int n) throws IOException, SAXException {
         final GpxData data = GpxReaderTest.parseGpxData(fileName);
+        data.getLayerPrefs().putAll(layerPrefs);
         final GpxLayer layer = new GpxLayer(data);
         final GpxDrawHelper gdh = new GpxDrawHelper(layer);
-        gdh.readPreferences(layerName);
+        gdh.readPreferences();
         gdh.calculateColors();
         return data.getTrackPoints().limit(n).map(p -> ColorHelper.color2html(p.customColoring)).collect(Collectors.toList());
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarkerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarkerTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/AudioMarkerTest.java	(revision 15496)
@@ -11,6 +11,4 @@
 import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.gpx.Extensions;
-import org.openstreetmap.josm.data.gpx.GpxConstants;
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.WayPoint;
@@ -46,6 +44,5 @@
         WayPoint wpt = marker.convertToWayPoint();
         assertEquals(LatLon.ZERO, wpt.getCoor());
-        Extensions ext = (Extensions) wpt.get(GpxConstants.META_EXTENSIONS);
-        assertEquals("2.0", ext.get("offset"));
+        assertEquals("2.0", wpt.getExtensions().get("josm", "offset").getValue());
     }
 }
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java	(revision 15496)
@@ -4,7 +4,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.awt.Color;
 import java.util.Arrays;
 
@@ -17,5 +17,8 @@
 import org.openstreetmap.josm.data.gpx.GpxLink;
 import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
@@ -29,5 +32,5 @@
 
     /**
-     * Setup tests
+     * For creating layers
      */
     @Rule
@@ -48,10 +51,9 @@
     @Test
     public void testMarkerLayer() {
-        assertEquals(Color.magenta, MarkerLayer.getGenericColor());
         MarkerLayer layer = new MarkerLayer(new GpxData(), "foo", null, null);
         MainApplication.getLayerManager().addLayer(layer);
 
         assertEquals("foo", layer.getName());
-        assertEquals(Color.magenta, layer.getColorProperty().get());
+        assertNull(layer.getColor());
         assertNotNull(layer.getIcon());
         assertEquals("0 markers", layer.getToolTipText());
@@ -62,13 +64,13 @@
         WayPoint wpt = new WayPoint(LatLon.ZERO);
         wpt.attr.put(GpxConstants.META_LINKS, Arrays.asList(new GpxLink("https://josm.openstreetmap.de")));
-        wpt.addExtension("offset", "1.0");
+        wpt.getExtensions().add("josm", "offset", "1.0");
         gpx.waypoints.add(wpt);
         wpt = new WayPoint(LatLon.ZERO);
-        wpt.addExtension("offset", "NaN");
+        wpt.getExtensions().add("josm", "offset", "NaN");
         gpx.waypoints.add(wpt);
         layer = new MarkerLayer(gpx, "bar", null, null);
 
         assertEquals("bar", layer.getName());
-        assertEquals(Color.magenta, layer.getColorProperty().get());
+        assertNull(layer.getColor());
         assertNotNull(layer.getIcon());
         assertEquals("3 markers", layer.getToolTipText());
@@ -76,3 +78,23 @@
         assertTrue(layer.getMenuEntries().length > 10);
     }
+
+    /**
+     * Unit test of {@code Main.map.mapView.playHeadMarker}.
+     */
+    @Test
+    public void testPlayHeadMarker() {
+        try {
+            MainApplication.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "", null));
+            MapFrame map = MainApplication.getMap();
+            MarkerLayer layer = new MarkerLayer(new GpxData(), null, null, null);
+            assertNull(map.mapView.playHeadMarker);
+            MainApplication.getLayerManager().addLayer(layer);
+            assertNotNull(map.mapView.playHeadMarker);
+            MainApplication.getLayerManager().removeLayer(layer);
+        } finally {
+            if (MainApplication.isDisplayingMapView()) {
+                MainApplication.getMap().mapView.playHeadMarker = null;
+            }
+        }
+    }
 }
Index: trunk/test/unit/org/openstreetmap/josm/io/GpxReaderTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/GpxReaderTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/io/GpxReaderTest.java	(revision 15496)
@@ -11,4 +11,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.junit.Rule;
@@ -70,4 +72,22 @@
 
     /**
+     * Tests if layer preferences can be read
+     * @throws Exception if track can't be parsed
+     */
+    @Test
+    public void testLayerPrefs() throws Exception {
+        final GpxData data = parseGpxData(TestUtils.getTestDataRoot() + "tracks/tracks-layerprefs.gpx");
+        Map<String, String> e = new HashMap<>();
+        e.put("colormode.velocity.tune", "10");
+        e.put("lines.arrows.min-distance", "20");
+        e.put("colormode", "1");
+        e.put("lines", "1");
+        e.put("lines.arrows", "true");
+        e.put("colormode.dynamic-range", "true");
+        e.put("colors", "0");
+        assertEquals(data.getLayerPrefs(), e);
+    }
+
+    /**
      * Tests invalid data.
      * @throws Exception always SAXException
Index: trunk/test/unit/org/openstreetmap/josm/io/GpxWriterTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/GpxWriterTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/io/GpxWriterTest.java	(revision 15496)
@@ -4,4 +4,5 @@
 import static org.junit.Assert.assertEquals;
 
+import java.awt.Color;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -10,5 +11,7 @@
 import java.time.Month;
 import java.time.ZoneOffset;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.function.Consumer;
 
@@ -18,4 +21,8 @@
 import org.openstreetmap.josm.data.gpx.GpxConstants;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxData.XMLNamespace;
+import org.openstreetmap.josm.data.gpx.GpxExtensionCollection;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
@@ -88,3 +95,185 @@
                 "    <pdop>1.2</pdop>%n");
     }
+
+    /**
+     * Tests if extensions are written correctly.
+     * @throws IOException
+     */
+    @Test
+    public void testExtensions() throws IOException {
+        GpxData data = new GpxData();
+        data.getNamespaces().add(new XMLNamespace("test", "http://example.com/testURI")); //only namespace, no location printed
+        data.getNamespaces().add(new XMLNamespace("knownprefix", "http://example.com/URI", "http://example.com/location.xsd")); //printed
+        data.getNamespaces().add(new XMLNamespace("notpresent", "http://example.com/notpresent", "http://example.com/notpresent.xsd")); //NOT printed
+
+        GpxExtensionCollection exts = data.getExtensions();
+        data.fromServer = true; //printed
+        data.getLayerPrefs().put("foo", "bar"); //printed depending on writer config
+        exts.add("knownprefix", "foo", "bar"); //printed
+        exts.add("unknownprefix", "foo", "bar"); //NOT printed
+
+        WayPoint wpt = new WayPoint(LatLon.ZERO);
+        wpt.getExtensions().add("test", "foo", "extension of a waypoint"); //printed
+
+        GpxTrackSegment seg = new GpxTrackSegment(Arrays.asList(wpt));
+        seg.getExtensions().add("test", "foo", "extension of a segment"); //printed
+
+        GpxTrack trk = new GpxTrack(Arrays.asList(seg), new HashMap<>());
+
+        trk.setColor(Color.RED); //printed depending on writer configuration
+
+        data.addTrack(trk);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        GpxWriter writer = new GpxWriter(baos);
+
+        writer.write(data);
+        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
+                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
+                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
+                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
+                "    xmlns:gpxd=\"http://josm.openstreetmap.de/gpx-drawing-extensions-1.0\"\n" +
+                "    xmlns:test=\"http://example.com/testURI\"\n" +
+                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd http://josm.openstreetmap.de/gpx-drawing-extensions-1.0 http://josm.openstreetmap.de/gpx-drawing-extensions-1.0.xsd\">\n" +
+                "  <metadata>\n" +
+                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
+                "    <extensions>\n" +
+                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
+                "      <josm:from-server>true</josm:from-server>\n" +
+                "      <josm:layerPreferences>\n" +
+                "        <josm:entry key=\"foo\" value=\"bar\"/>\n" +
+                "      </josm:layerPreferences>\n" +
+                "    </extensions>\n" +
+                "  </metadata>\n" +
+                "  <trk>\n" +
+                "    <extensions>\n" +
+                "      <gpxd:color>#FF0000</gpxd:color>\n" +
+                "    </extensions>\n" +
+                "    <trkseg>\n" +
+                "      <extensions>\n" +
+                "        <test:foo>extension of a segment</test:foo>\n" +
+                "      </extensions>\n" +
+                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
+                "        <extensions>\n" +
+                "          <test:foo>extension of a waypoint</test:foo>\n" +
+                "        </extensions>\n" +
+                "      </trkpt>\n" +
+                "    </trkseg>\n" +
+                "  </trk>\n" +
+                "</gpx>", baos.toString());
+
+        baos.reset();
+        writer.write(data, GpxConstants.ColorFormat.GPXX, true);
+        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
+                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
+                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
+                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
+                "    xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\"\n" +
+                "    xmlns:test=\"http://example.com/testURI\"\n" +
+                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd\">\n" +
+                "  <metadata>\n" +
+                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
+                "    <extensions>\n" +
+                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
+                "      <josm:from-server>true</josm:from-server>\n" +
+                "      <josm:layerPreferences>\n" +
+                "        <josm:entry key=\"foo\" value=\"bar\"/>\n" +
+                "      </josm:layerPreferences>\n" +
+                "    </extensions>\n" +
+                "  </metadata>\n" +
+                "  <trk>\n" +
+                "    <extensions>\n" +
+                "      <gpxx:TrackExtensions>\n" +
+                "        <gpxx:DisplayColor>Red</gpxx:DisplayColor>\n" +
+                "      </gpxx:TrackExtensions>\n" +
+                "    </extensions>\n" +
+                "    <trkseg>\n" +
+                "      <extensions>\n" +
+                "        <test:foo>extension of a segment</test:foo>\n" +
+                "      </extensions>\n" +
+                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
+                "        <extensions>\n" +
+                "          <test:foo>extension of a waypoint</test:foo>\n" +
+                "        </extensions>\n" +
+                "      </trkpt>\n" +
+                "    </trkseg>\n" +
+                "  </trk>\n" +
+                "</gpx>", baos.toString());
+
+        baos.reset();
+        writer.write(data, null, false);
+        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
+                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
+                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
+                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
+                "    xmlns:test=\"http://example.com/testURI\"\n" +
+                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd\">\n" +
+                "  <metadata>\n" +
+                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
+                "    <extensions>\n" +
+                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
+                "      <josm:from-server>true</josm:from-server>\n" +
+                "    </extensions>\n" +
+                "  </metadata>\n" +
+                "  <trk>\n" +
+                "    <trkseg>\n" +
+                "      <extensions>\n" +
+                "        <test:foo>extension of a segment</test:foo>\n" +
+                "      </extensions>\n" +
+                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
+                "        <extensions>\n" +
+                "          <test:foo>extension of a waypoint</test:foo>\n" +
+                "        </extensions>\n" +
+                "      </trkpt>\n" +
+                "    </trkseg>\n" +
+                "  </trk>\n" +
+                "</gpx>", baos.toString());
+
+        baos.reset();
+        writer.write(data, GpxConstants.ColorFormat.GPXX, true);
+        // checked again to make sure that extensions are shown again after
+        // being hidden, even if they don't actually have to be converted
+        // (GPXD -> convertColor() -> GPXX -> hide() -> null -> show() -> GPXX)
+        assertEquals("<?xml version='1.0' encoding='UTF-8'?>\n" +
+                "<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
+                "    xmlns:knownprefix=\"http://example.com/URI\"\n" +
+                "    xmlns:josm=\"http://josm.openstreetmap.de/gpx-extensions-1.1\"\n" +
+                "    xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\"\n" +
+                "    xmlns:test=\"http://example.com/testURI\"\n" +
+                "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+                "    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://example.com/URI http://example.com/location.xsd http://josm.openstreetmap.de/gpx-extensions-1.1 http://josm.openstreetmap.de/gpx-extensions-1.1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd\">\n" +
+                "  <metadata>\n" +
+                "    <bounds minlat=\"0.0\" minlon=\"0.0\" maxlat=\"0.0\" maxlon=\"0.0\"/>\n" +
+                "    <extensions>\n" +
+                "      <knownprefix:foo>bar</knownprefix:foo>\n" +
+                "      <josm:from-server>true</josm:from-server>\n" +
+                "      <josm:layerPreferences>\n" +
+                "        <josm:entry key=\"foo\" value=\"bar\"/>\n" +
+                "      </josm:layerPreferences>\n" +
+                "    </extensions>\n" +
+                "  </metadata>\n" +
+                "  <trk>\n" +
+                "    <extensions>\n" +
+                "      <gpxx:TrackExtensions>\n" +
+                "        <gpxx:DisplayColor>Red</gpxx:DisplayColor>\n" +
+                "      </gpxx:TrackExtensions>\n" +
+                "    </extensions>\n" +
+                "    <trkseg>\n" +
+                "      <extensions>\n" +
+                "        <test:foo>extension of a segment</test:foo>\n" +
+                "      </extensions>\n" +
+                "      <trkpt lat=\"0.0\" lon=\"0.0\">\n" +
+                "        <extensions>\n" +
+                "          <test:foo>extension of a waypoint</test:foo>\n" +
+                "        </extensions>\n" +
+                "      </trkpt>\n" +
+                "    </trkseg>\n" +
+                "  </trk>\n" +
+                "</gpx>", baos.toString());
+
+        writer.close();
+    }
 }
Index: trunk/test/unit/org/openstreetmap/josm/io/nmea/NmeaReaderTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/nmea/NmeaReaderTest.java	(revision 15487)
+++ trunk/test/unit/org/openstreetmap/josm/io/nmea/NmeaReaderTest.java	(revision 15496)
@@ -25,5 +25,5 @@
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
-import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
+import org.openstreetmap.josm.data.gpx.IGpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.io.GpxReaderTest;
@@ -101,6 +101,6 @@
         assertEquals(nTracks, nmeaTrack.getSegments().size());
         if (nTracks > 0) {
-            GpxTrackSegment gpxSeg = gpxTrack.getSegments().iterator().next();
-            GpxTrackSegment nmeaSeg = nmeaTrack.getSegments().iterator().next();
+            IGpxTrackSegment gpxSeg = gpxTrack.getSegments().iterator().next();
+            IGpxTrackSegment nmeaSeg = nmeaTrack.getSegments().iterator().next();
             assertEquals(gpxSeg.getBounds(), nmeaSeg.getBounds());
             assertEquals(numCoor, gpxSeg.getWayPoints().size());
