Index: trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/gui/io/CredentialDialog.java	(revision 4645)
@@ -57,5 +57,5 @@
 
     private boolean canceled;
-    private CredentialPanel pnlCredentials;
+    protected CredentialPanel pnlCredentials;
     String saveUsernameAndPasswordCheckboxText;
 
@@ -135,5 +135,5 @@
     }
 
-    private static class CredentialPanel extends JPanel {
+    protected static class CredentialPanel extends JPanel {
         protected JTextField tfUserName;
         protected JPasswordField tfPassword;
Index: trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 4645)
@@ -31,6 +31,6 @@
 import org.openstreetmap.josm.data.osm.IPrimitive;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -107,7 +107,4 @@
      */
     private boolean initialized = false;
-
-    private StringWriter swriter = new StringWriter();
-    private OsmWriter osmWriter = new OsmWriter(new PrintWriter(swriter), true, null);
 
     /**
@@ -175,8 +172,7 @@
                     serverUrl,
                     version));
-            osmWriter.setVersion(version);
             initialized = true;
 
-            /* This is an interim solution for openstreetmap.org not currently 
+            /* This is an interim solution for openstreetmap.org not currently
              * transmitting their imagery blacklist in the capabilities call.
              * remove this as soon as openstreetmap.org adds blacklists. */
@@ -191,5 +187,5 @@
             /* This checks if there are any layers currently displayed that
              * are now on the blacklist, and removes them. This is a rare
-             * situaton - probably only occurs if the user changes the API URL
+             * situation - probably only occurs if the user changes the API URL
              * in the preferences menu. Otherwise they would not have been able
              * to load the layers in the first place becuase they would have
@@ -228,4 +224,6 @@
      */
     private String toXml(IPrimitive o, boolean addBody) {
+        StringWriter swriter = new StringWriter();
+        OsmWriter osmWriter = OsmWriterFactory.createOsmWriter(new PrintWriter(swriter), true, version);
         swriter.getBuffer().setLength(0);
         osmWriter.setWithBody(addBody);
@@ -234,5 +232,5 @@
         o.visit(osmWriter);
         osmWriter.footer();
-        osmWriter.out.flush();
+        osmWriter.flush();
         return swriter.toString();
     }
@@ -245,9 +243,11 @@
      */
     private String toXml(Changeset s) {
+        StringWriter swriter = new StringWriter();
+        OsmWriter osmWriter = OsmWriterFactory.createOsmWriter(new PrintWriter(swriter), true, version);
         swriter.getBuffer().setLength(0);
         osmWriter.header();
         osmWriter.visit(s);
         osmWriter.footer();
-        osmWriter.out.flush();
+        osmWriter.flush();
         return swriter.toString();
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmChangeBuilder.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmChangeBuilder.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/OsmChangeBuilder.java	(revision 4645)
@@ -34,5 +34,5 @@
         this.apiVersion = apiVersion == null ? DEFAULT_API_VERSION : apiVersion;
         writer = new PrintWriter(swriter = new StringWriter());
-        osmwriter = new OsmWriter(writer, false, apiVersion);
+        osmwriter = OsmWriterFactory.createOsmWriter(writer, false, apiVersion);
         osmwriter.setChangeset(changeset);
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmExporter.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/OsmExporter.java	(revision 4645)
@@ -67,5 +67,5 @@
             Writer writer = new OutputStreamWriter(out, "UTF-8");
 
-            OsmWriter w = new OsmWriter(new PrintWriter(writer), false, layer.data.getVersion());
+            OsmWriter w = OsmWriterFactory.createOsmWriter(new PrintWriter(writer), false, layer.data.getVersion());
             layer.data.getReadLock().lock();
             try {
Index: trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 4645)
@@ -50,4 +50,22 @@
     protected XMLStreamReader parser;
 
+    /** Used by plugins to register themselves as data postprocessors. */
+    public static ArrayList<OsmServerReadPostprocessor> postprocessors;
+
+    /** register a new postprocessor */
+    public static void registerPostprocessor(OsmServerReadPostprocessor pp) {
+        if (postprocessors == null) {
+            postprocessors = new ArrayList<OsmServerReadPostprocessor>();
+        }
+        postprocessors.add(pp);
+    }
+
+    /** deregister a postprocessor previously registered with registerPostprocessor */
+    public static void deregisterPostprocessor(OsmServerReadPostprocessor pp) {
+        if (postprocessors != null) {
+            postprocessors.remove(pp);
+        }
+    }
+
     /**
      * constructor (for private and subclasses use only)
@@ -71,7 +89,6 @@
             if (event == XMLStreamConstants.START_ELEMENT) {
                 parseRoot();
-            } else if (event == XMLStreamConstants.END_ELEMENT) {
+            } else if (event == XMLStreamConstants.END_ELEMENT)
                 return;
-            }
             if (parser.hasNext()) {
                 event = parser.next();
@@ -82,5 +99,5 @@
         parser.close();
     }
-    
+
     protected void parseRoot() throws XMLStreamException {
         if (parser.getLocalName().equals("osm")) {
@@ -121,7 +138,6 @@
                     parseUnknown();
                 }
-            } else if (event == XMLStreamConstants.END_ELEMENT) {
+            } else if (event == XMLStreamConstants.END_ELEMENT)
                 return;
-            }
         }
     }
@@ -172,7 +188,6 @@
                     parseUnknown();
                 }
-            } else if (event == XMLStreamConstants.END_ELEMENT) {
+            } else if (event == XMLStreamConstants.END_ELEMENT)
                 return n;
-            }
         }
     }
@@ -301,7 +316,6 @@
                         parseUnknown();
                     }
-                } else if (event == XMLStreamConstants.END_ELEMENT) {
+                } else if (event == XMLStreamConstants.END_ELEMENT)
                     return;
-                }
             }
         } else {
@@ -328,7 +342,6 @@
             if (event == XMLStreamConstants.START_ELEMENT) {
                 parseUnknown(false); /* no more warning for inner elements */
-            } else if (event == XMLStreamConstants.END_ELEMENT) {
+            } else if (event == XMLStreamConstants.END_ELEMENT)
                 return;
-            }
         }
     }
@@ -350,7 +363,6 @@
             if (event == XMLStreamConstants.START_ELEMENT) {
                 parseUnknown(printWarning);
-            } else if (event == XMLStreamConstants.END_ELEMENT) {
+            } else if (event == XMLStreamConstants.END_ELEMENT)
                 return;
-            }
         }
     }
@@ -555,4 +567,12 @@
             prepareDataSet();
             progressMonitor.worked(1);
+
+            // iterate over registered postprocessors and give them each a chance
+            // to modify the dataset we have just loaded.
+            if (postprocessors != null) {
+                for (OsmServerReadPostprocessor pp : postprocessors) {
+                    pp.postprocessDataSet(getDataSet(), progressMonitor);
+                }
+            }
             return getDataSet();
         } catch(IllegalDataException e) {
@@ -567,9 +587,8 @@
                 msg = m.group(1);
             }
-            if (e.getLocation() != null) {
+            if (e.getLocation() != null)
                 throw new IllegalDataException(tr("Line {0} column {1}: ", e.getLocation().getLineNumber(), e.getLocation().getColumnNumber()) + msg, e);
-            } else {
+            else
                 throw new IllegalDataException(msg, e);
-            }
         } catch(Exception e) {
             throw new IllegalDataException(e);
@@ -578,5 +597,5 @@
         }
     }
-    
+
     /**
      * Parse the given input source and return the dataset.
Index: trunk/src/org/openstreetmap/josm/io/OsmServerReadPostprocessor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerReadPostprocessor.java	(revision 4645)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerReadPostprocessor.java	(revision 4645)
@@ -0,0 +1,11 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public interface OsmServerReadPostprocessor {
+
+    public void postprocessDataSet(DataSet ds, ProgressMonitor progress);
+
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmServerWritePostprocessor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerWritePostprocessor.java	(revision 4645)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerWritePostprocessor.java	(revision 4645)
@@ -0,0 +1,13 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+
+public interface OsmServerWritePostprocessor {
+
+    public void postprocessUploadedPrimitives(Collection<IPrimitive> p, ProgressMonitor progress);
+
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 4645)
@@ -36,4 +36,17 @@
      */
     private Collection<IPrimitive> processed;
+
+    private static ArrayList<OsmServerWritePostprocessor> postprocessors;
+    public static void registerPostprocessor(OsmServerWritePostprocessor pp) {
+        if (postprocessors == null) {
+            postprocessors = new ArrayList<OsmServerWritePostprocessor>();
+        }
+        postprocessors.add(pp);
+    }
+    public static void unregisterPostprocessor(OsmServerWritePostprocessor pp) {
+        if (postprocessors != null) {
+            postprocessors.remove(pp);
+        }
+    }
 
     private OsmApi api = OsmApi.getOsmApi();
@@ -205,4 +218,5 @@
             throw e;
         } finally {
+            executePostprocessors(monitor);
             monitor.finishTask();
             api.setChangeset(null);
@@ -216,5 +230,5 @@
             api.createPrimitive(osm, progressMonitor);
         } else {
-            api.modifyPrimitive(osm,progressMonitor);
+            api.modifyPrimitive(osm, progressMonitor);
         }
     }
@@ -235,3 +249,14 @@
         return processed;
     }
+
+    /**
+     * Calls all registered upload postprocessors.
+     */
+    public void executePostprocessors(ProgressMonitor pm) {
+        if (postprocessors != null) {
+            for (OsmServerWritePostprocessor pp : postprocessors) {
+                pp.postprocessUploadedPrimitives(processed, pm);
+            }
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 4645)
@@ -42,5 +42,8 @@
     private Changeset changeset;
 
-    public OsmWriter(PrintWriter out, boolean osmConform, String version) {
+    /**
+     * Do not call this directly. Use OsmWriterFactory instead.
+     */
+    protected OsmWriter(PrintWriter out, boolean osmConform, String version) {
         super(out);
         this.osmConform = osmConform;
@@ -68,5 +71,5 @@
     }
 
-    private static final Comparator<OsmPrimitive> byIdComparator = new Comparator<OsmPrimitive>() {
+    protected static final Comparator<OsmPrimitive> byIdComparator = new Comparator<OsmPrimitive>() {
         @Override public int compare(OsmPrimitive o1, OsmPrimitive o2) {
             return (o1.getUniqueId()<o2.getUniqueId() ? -1 : (o1.getUniqueId()==o2.getUniqueId() ? 0 : 1));
@@ -74,5 +77,5 @@
     };
 
-    private Collection<OsmPrimitive> sortById(Collection<? extends OsmPrimitive> primitives) {
+    protected Collection<OsmPrimitive> sortById(Collection<? extends OsmPrimitive> primitives) {
         List<OsmPrimitive> result = new ArrayList<OsmPrimitive>(primitives.size());
         result.addAll(primitives);
@@ -99,5 +102,5 @@
     }
 
-    private boolean shouldWrite(OsmPrimitive osm) {
+    protected boolean shouldWrite(OsmPrimitive osm) {
         return !osm.isNewOrUndeleted() || !osm.isDeleted();
     }
@@ -185,5 +188,5 @@
     }
 
-    private static final Comparator<Entry<String, String>> byKeyComparator = new Comparator<Entry<String,String>>() {
+    protected static final Comparator<Entry<String, String>> byKeyComparator = new Comparator<Entry<String,String>>() {
         @Override public int compare(Entry<String, String> o1, Entry<String, String> o2) {
             return o1.getKey().compareTo(o2.getKey());
@@ -191,5 +194,5 @@
     };
 
-    private void addTags(Tagged osm, String tagname, boolean tagOpen) {
+    protected void addTags(Tagged osm, String tagname, boolean tagOpen) {
         if (osm.hasKeys()) {
             if (tagOpen) {
@@ -216,5 +219,5 @@
      * id, action, user, and visible.
      */
-    private void addCommon(IPrimitive osm, String tagname) {
+    protected void addCommon(IPrimitive osm, String tagname) {
         out.print("  <"+tagname);
         if (osm.getUniqueId() != 0) {
@@ -261,4 +264,5 @@
     }
 
+    @Override
     public void flush() {
         out.flush();
Index: trunk/src/org/openstreetmap/josm/io/OsmWriterFactory.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmWriterFactory.java	(revision 4645)
+++ trunk/src/org/openstreetmap/josm/io/OsmWriterFactory.java	(revision 4645)
@@ -0,0 +1,32 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import java.io.PrintWriter;
+
+/**
+ * This factory is called by everyone who needs an OsmWriter object,
+ * instead of directly calling the OsmWriter constructor.
+ * 
+ * This enables plugins to substitute the original OsmWriter with
+ * their own version, altering the way JOSM writes objects to the
+ * server, and to disk.
+ * 
+ * @author Frederik Ramm
+ *
+ */
+public class OsmWriterFactory {
+
+    public static OsmWriterFactory theFactory;
+    public static OsmWriter createOsmWriter(PrintWriter out, boolean osmConform, String version) {
+        // pre-set factory with this default implementation; can still be overwritten
+        // later. note that the default factory may already be used for constructing
+        // OsmWriters during the startup process.
+        if (theFactory == null) {
+            theFactory = new OsmWriterFactory();
+        }
+        return theFactory.createOsmWriterImpl(out, osmConform, version);
+    }
+    protected OsmWriter createOsmWriterImpl(PrintWriter out, boolean osmConform, String version) {
+        return new OsmWriter(out, osmConform, version);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/XmlWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/XmlWriter.java	(revision 4644)
+++ trunk/src/org/openstreetmap/josm/io/XmlWriter.java	(revision 4645)
@@ -18,4 +18,8 @@
     }
 
+    public void flush() {
+        out.flush();
+    }
+
     /**
      * Encode the given string in XML1.0 format.
@@ -27,9 +31,11 @@
             String encS = XmlWriter.encoding.get(unencoded.charAt(i));
             if (encS != null) {
-                if (buffer == null)
+                if (buffer == null) {
                     buffer = new StringBuilder(unencoded.substring(0,i));
+                }
                 buffer.append(encS);
-            } else if (buffer != null)
+            } else if (buffer != null) {
                 buffer.append(unencoded.charAt(i));
+            }
         }
         return (buffer == null) ? unencoded : buffer.toString();
