Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 13434)
@@ -17,4 +17,5 @@
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
@@ -103,5 +104,5 @@
  * @author imi
  */
-public final class DataSet extends QuadBucketPrimitiveStore implements Data, ProjectionChangeListener {
+public final class DataSet extends QuadBucketPrimitiveStore implements Data, ProjectionChangeListener, ReadOnly {
 
     /**
@@ -143,4 +144,20 @@
             return xmlFlag;
         }
+
+        /**
+         * Returns the {@code UploadPolicy} for the given <code>upload='...'</code> XML-attribute
+         * @param xmlFlag <code>upload='...'</code> XML-attribute to convert
+         * @return {@code UploadPolicy} value
+         * @throws IllegalArgumentException for invalid values
+         * @since 13434
+         */
+        public static UploadPolicy of(String xmlFlag) {
+            for (UploadPolicy policy : values()) {
+                if (policy.getXmlFlag().equalsIgnoreCase(xmlFlag)) {
+                    return policy;
+                }
+            }
+            throw new IllegalArgumentException(xmlFlag);
+        }
     }
 
@@ -171,4 +188,6 @@
     private String name;
     private UploadPolicy uploadPolicy;
+    /** Flag used to know if the dataset should not be editable */
+    private final AtomicBoolean isReadOnly = new AtomicBoolean(false);
 
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
@@ -259,4 +278,5 @@
             version = copyFrom.version;
             uploadPolicy = copyFrom.uploadPolicy;
+            isReadOnly.set(copyFrom.isReadOnly.get());
         } finally {
             copyFrom.getReadLock().unlock();
@@ -353,6 +373,8 @@
      *
      * @param version the API version, i.e. "0.6"
+     * @throws IllegalStateException if the dataset is read-only
      */
     public void setVersion(String version) {
+        checkModifiable();
         this.version = version;
     }
@@ -533,8 +555,10 @@
      *
      * @param primitive the primitive.
+     * @throws IllegalStateException if the dataset is read-only
      */
     @Override
     public void addPrimitive(OsmPrimitive primitive) {
         Objects.requireNonNull(primitive, "primitive");
+        checkModifiable();
         beginUpdate();
         try {
@@ -561,6 +585,8 @@
      *
      * @param primitiveId the id of the primitive
+     * @throws IllegalStateException if the dataset is read-only
      */
     public void removePrimitive(PrimitiveId primitiveId) {
+        checkModifiable();
         beginUpdate();
         try {
@@ -587,4 +613,5 @@
     @Override
     protected void removePrimitive(OsmPrimitive primitive) {
+        checkModifiable();
         beginUpdate();
         try {
@@ -998,6 +1025,8 @@
      * @param node the node
      * @return The set of ways that have been modified
+     * @throws IllegalStateException if the dataset is read-only
      */
     public Set<Way> unlinkNodeFromWays(Node node) {
+        checkModifiable();
         Set<Way> result = new HashSet<>();
         beginUpdate();
@@ -1025,6 +1054,8 @@
      * @param primitive the primitive
      * @return The set of relations that have been modified
+     * @throws IllegalStateException if the dataset is read-only
      */
     public Set<Relation> unlinkPrimitiveFromRelations(OsmPrimitive primitive) {
+        checkModifiable();
         Set<Relation> result = new HashSet<>();
         beginUpdate();
@@ -1059,6 +1090,8 @@
      * @param referencedPrimitive the referenced primitive
      * @return The set of primitives that have been modified
+     * @throws IllegalStateException if the dataset is read-only
      */
     public Set<OsmPrimitive> unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) {
+        checkModifiable();
         Set<OsmPrimitive> result = new HashSet<>();
         beginUpdate();
@@ -1131,4 +1164,5 @@
      * }
      * </pre>
+     * @see #endUpdate()
      */
     public void beginUpdate() {
@@ -1138,4 +1172,15 @@
 
     /**
+     * Must be called after a previous call to {@link #beginUpdate()} to fire change events.
+     * <br>
+     * Typical usecase should look like this:
+     * <pre>
+     * ds.beginUpdate();
+     * try {
+     *   ...
+     * } finally {
+     *   ds.endUpdate();
+     * }
+     * </pre>
      * @see DataSet#beginUpdate()
      */
@@ -1273,7 +1318,9 @@
      * Removes all primitives from the dataset and resets the currently selected primitives
      * to the empty collection. Also notifies selection change listeners if necessary.
+     * @throws IllegalStateException if the dataset is read-only
      */
     @Override
     public void clear() {
+        checkModifiable();
         beginUpdate();
         try {
@@ -1292,7 +1339,8 @@
      * Marks all "invisible" objects as deleted. These objects should be always marked as
      * deleted when downloaded from the server. They can be undeleted later if necessary.
-     *
+     * @throws IllegalStateException if the dataset is read-only
      */
     public void deleteInvisible() {
+        checkModifiable();
         for (OsmPrimitive primitive:allPrimitives) {
             if (!primitive.isVisible()) {
@@ -1314,7 +1362,9 @@
      * @param from The source DataSet
      * @param progressMonitor The progress monitor
+     * @throws IllegalStateException if the dataset is read-only
      */
     public synchronized void mergeFrom(DataSet from, ProgressMonitor progressMonitor) {
         if (from != null) {
+            checkModifiable();
             new DataSetMerger(this, from).merge(progressMonitor);
             synchronized (from) {
@@ -1402,3 +1452,32 @@
         mappaintCacheIdx++;
     }
+
+    @Override
+    public void setReadOnly() {
+        if (!isReadOnly.compareAndSet(false, true)) {
+            Logging.warn("Trying to set readOnly flag on a readOnly dataset ", getName());
+        }
+    }
+
+    @Override
+    public void unsetReadOnly() {
+        if (!isReadOnly.compareAndSet(true, false)) {
+            Logging.warn("Trying to unset readOnly flag on a non-readOnly dataset ", getName());
+        }
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return isReadOnly.get();
+    }
+
+    /**
+     * Checks the dataset is modifiable (not read-only).
+     * @throws IllegalStateException if the dataset is read-only
+     */
+    private void checkModifiable() {
+        if (isReadOnly()) {
+            throw new IllegalStateException("DataSet is read-only");
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java	(revision 13434)
@@ -98,5 +98,5 @@
      */
     public void executeFilters() {
-        DataSet ds = Main.main.getEditDataSet();
+        DataSet ds = Main.main.getActiveDataSet();
         changed = false;
         if (ds == null) {
Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 13434)
@@ -275,4 +275,12 @@
     }
 
+    /**
+     * Throws exception if primitive is in a read-only dataset
+     */
+    protected final void checkDatasetNotReadOnly() {
+        if (dataSet != null && dataSet.isReadOnly())
+            throw new DataIntegrityProblemException("Primitive cannot be modified in read-only dataset: " + toString());
+    }
+
     protected boolean writeLock() {
         if (dataSet != null) {
@@ -305,4 +313,5 @@
     @Override
     public void setOsmId(long id, int version) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -343,4 +352,5 @@
     @Override
     public void setUser(User user) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -353,4 +363,5 @@
     @Override
     public void setChangesetId(int changesetId) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -367,4 +378,5 @@
     @Override
     public void setTimestamp(Date timestamp) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -534,4 +546,5 @@
     @Override
     public void setModified(boolean modified) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -548,4 +561,5 @@
     @Override
     public void setVisible(boolean visible) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -559,4 +573,5 @@
     @Override
     public void setDeleted(boolean deleted) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -577,4 +592,5 @@
     @Override
     protected final void setIncomplete(boolean incomplete) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -891,4 +907,5 @@
     @Override
     public final void setKeys(TagMap keys) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -901,4 +918,5 @@
     @Override
     public final void setKeys(Map<String, String> keys) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -911,4 +929,5 @@
     @Override
     public final void put(String key, String value) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -921,4 +940,5 @@
     @Override
     public final void remove(String key) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -931,4 +951,5 @@
     @Override
     public final void removeAll() {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -966,4 +987,5 @@
      */
     protected void addReferrer(OsmPrimitive referrer) {
+        checkDatasetNotReadOnly();
         if (referrers == null) {
             referrers = referrer;
@@ -986,4 +1008,5 @@
      */
     protected void removeReferrer(OsmPrimitive referrer) {
+        checkDatasetNotReadOnly();
         if (referrers instanceof OsmPrimitive) {
             if (referrers == referrer) {
@@ -1148,4 +1171,5 @@
      */
     public void mergeFrom(OsmPrimitive other) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -1239,4 +1263,5 @@
      */
     public void load(PrimitiveData data) {
+        checkDatasetNotReadOnly();
         // Write lock is provided by subclasses
         setKeys(data.hasKeys() ? data.getKeys() : null);
Index: trunk/src/org/openstreetmap/josm/data/osm/ReadOnly.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/ReadOnly.java	(revision 13434)
+++ trunk/src/org/openstreetmap/josm/data/osm/ReadOnly.java	(revision 13434)
@@ -0,0 +1,25 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+/**
+ * To be implemented by modifiable objects to offer a "read-only" mode.
+ * @since 13434
+ */
+public interface ReadOnly {
+
+    /**
+     * Enables the read-only mode.
+     */
+    void setReadOnly();
+
+    /**
+     * Disables the read-only mode.
+     */
+    void unsetReadOnly();
+
+    /**
+     * Determines if this is read-only (thus it cannot be modified).
+     * @return {@code true} if this is read-only
+     */
+    boolean isReadOnly();
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/Relation.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 13434)
@@ -46,4 +46,5 @@
      */
     public void setMembers(List<RelationMember> members) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -88,4 +89,5 @@
      */
     public void addMember(RelationMember member) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -105,4 +107,5 @@
      */
     public void addMember(int index, RelationMember member) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -127,4 +130,5 @@
      */
     public RelationMember setMember(int index, RelationMember member) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -150,4 +154,5 @@
      */
     public RelationMember removeMember(int index) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -376,4 +381,5 @@
      */
     public void removeMembersFor(Collection<? extends OsmPrimitive> primitives) {
+        checkDatasetNotReadOnly();
         if (primitives == null || primitives.isEmpty())
             return;
Index: trunk/src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/Way.java	(revision 13434)
@@ -53,4 +53,5 @@
      */
     public void setNodes(List<Node> nodes) {
+        checkDatasetNotReadOnly();
         boolean locked = writeLock();
         try {
@@ -361,4 +362,5 @@
      */
     public void removeNode(Node n) {
+        checkDatasetNotReadOnly();
         if (n == null || isIncomplete()) return;
         boolean locked = writeLock();
@@ -389,4 +391,5 @@
      */
     public void removeNodes(Set<? extends Node> selection) {
+        checkDatasetNotReadOnly();
         if (selection == null || isIncomplete()) return;
         boolean locked = writeLock();
@@ -425,4 +428,5 @@
      */
     public void addNode(Node n) {
+        checkDatasetNotReadOnly();
         if (n == null) return;
 
@@ -452,4 +456,5 @@
      */
     public void addNode(int offs, Node n) {
+        checkDatasetNotReadOnly();
         if (n == null) return;
 
Index: trunk/src/org/openstreetmap/josm/data/osm/event/DatasetEventManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/event/DatasetEventManager.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/event/DatasetEventManager.java	(revision 13434)
@@ -182,10 +182,10 @@
     @Override
     public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
-        DataSet oldData = e.getPreviousEditDataSet();
+        DataSet oldData = e.getPreviousDataSet();
         if (oldData != null) {
             oldData.removeDataSetListener(myListener);
         }
 
-        DataSet newData = e.getSource().getEditDataSet();
+        DataSet newData = e.getSource().getActiveDataSet();
         if (newData != null) {
             newData.addDataSetListener(myListener);
Index: trunk/src/org/openstreetmap/josm/data/osm/event/SelectionEventManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/event/SelectionEventManager.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/osm/event/SelectionEventManager.java	(revision 13434)
@@ -184,5 +184,5 @@
     @Override
     public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
-        DataSet oldDataSet = e.getPreviousEditDataSet();
+        DataSet oldDataSet = e.getPreviousDataSet();
         if (oldDataSet != null) {
             // Fake a selection removal
@@ -194,5 +194,5 @@
             oldDataSet.removeSelectionListener(this);
         }
-        DataSet newDataSet = e.getSource().getEditDataSet();
+        DataSet newDataSet = e.getSource().getActiveDataSet();
         if (newDataSet != null) {
             newDataSet.addSelectionListener(this);
Index: trunk/src/org/openstreetmap/josm/data/projection/Projections.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/projection/Projections.java	(revision 13434)
@@ -320,5 +320,5 @@
                 cproj.update(pd.definition);
             } catch (ProjectionConfigurationException ex) {
-                throw new RuntimeException("Error loading " + code, ex);
+                throw new JosmRuntimeException("Error loading " + code, ex);
             }
             proj = cproj;
Index: trunk/src/org/openstreetmap/josm/data/validation/TestError.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/TestError.java	(revision 13430)
+++ trunk/src/org/openstreetmap/josm/data/validation/TestError.java	(revision 13434)
@@ -12,4 +12,5 @@
 
 import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -358,5 +359,6 @@
      */
     public boolean isFixable() {
-        return fixingCommand != null || ((tester != null) && tester.isFixable(this));
+        return (fixingCommand != null || ((tester != null) && tester.isFixable(this)))
+                && primitives.stream().map(OsmPrimitive::getDataSet).noneMatch(DataSet::isReadOnly);
     }
 
