Index: trunk/src/org/openstreetmap/josm/io/OsmServerReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 12508)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 12510)
@@ -10,4 +10,6 @@
 import java.net.URL;
 import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
 
 import org.openstreetmap.josm.Main;
@@ -19,4 +21,9 @@
 import org.openstreetmap.josm.io.auth.CredentialsManager;
 import org.openstreetmap.josm.tools.HttpClient;
+import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.XmlParsingException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
 
 /**
@@ -360,3 +367,59 @@
         return null;
     }
+
+    /**
+     * Returns an attribute from the given DOM node.
+     * @param node DOM node
+     * @param name attribute name
+     * @return attribute value for the given attribute
+     * @since 12510
+     */
+    protected static String getAttribute(Node node, String name) {
+        return node.getAttributes().getNamedItem(name).getNodeValue();
+    }
+
+    /**
+     * DOM document parser.
+     * @param <R> resulting type
+     * @since 12510
+     */
+    @FunctionalInterface
+    protected interface DomParser<R> {
+        /**
+         * Parses a given DOM document.
+         * @param doc DOM document
+         * @return parsed data
+         * @throws XmlParsingException if an XML parsing error occurs
+         */
+        R parse(Document doc) throws XmlParsingException;
+    }
+
+    /**
+     * Fetches generic data from the DOM document resulting an API call.
+     * @param api the OSM API call
+     * @param subtask the subtask translated message
+     * @param parser the parser converting the DOM document (OSM API result)
+     * @param <T> data type
+     * @param monitor The progress monitor
+     * @param reason The reason to show on console. Can be {@code null} if no reason is given
+     * @return The converted data
+     * @throws OsmTransferException if something goes wrong
+     * @since 12510
+     */
+    public <T> T fetchData(String api, String subtask, DomParser<T> parser, ProgressMonitor monitor, String reason)
+            throws OsmTransferException {
+        try {
+            monitor.beginTask("");
+            monitor.indeterminateSubTask(subtask);
+            try (InputStream in = getInputStream(api, monitor.createSubTaskMonitor(1, true), reason)) {
+                return parser.parse(Utils.parseSafeDOM(in));
+            }
+        } catch (OsmTransferException e) {
+            throw e;
+        } catch (IOException | ParserConfigurationException | SAXException e) {
+            throw new OsmTransferException(e);
+        } finally {
+            monitor.finishTask();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java	(revision 12508)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java	(revision 12510)
@@ -4,10 +4,7 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.LinkedList;
 import java.util.List;
 
-import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
@@ -19,5 +16,4 @@
 import org.openstreetmap.josm.data.osm.UserInfo;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.XmlParsingException;
 import org.openstreetmap.josm.tools.date.DateUtils;
@@ -25,5 +21,4 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
 
 /**
@@ -32,8 +27,4 @@
  */
 public class OsmServerUserInfoReader extends OsmServerReader {
-
-    protected static String getAttribute(Node node, String name) {
-        return node.getAttributes().getNamedItem(name).getNodeValue();
-    }
 
     /**
@@ -175,17 +166,6 @@
      */
     public UserInfo fetchUserInfo(ProgressMonitor monitor, String reason) throws OsmTransferException {
-        try {
-            monitor.beginTask("");
-            monitor.indeterminateSubTask(tr("Reading user info ..."));
-            try (InputStream in = getInputStream("user/details", monitor.createSubTaskMonitor(1, true), reason)) {
-                return buildFromXML(Utils.parseSafeDOM(in));
-            }
-        } catch (OsmTransferException e) {
-            throw e;
-        } catch (IOException | ParserConfigurationException | SAXException e) {
-            throw new OsmTransferException(e);
-        } finally {
-            monitor.finishTask();
-        }
+        return fetchData("user/details", tr("Reading user info ..."),
+                OsmServerUserInfoReader::buildFromXML, monitor, reason);
     }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerUserPreferencesReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerUserPreferencesReader.java	(revision 12508)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerUserPreferencesReader.java	(revision 12510)
@@ -4,10 +4,7 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
@@ -17,10 +14,8 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.XmlParsingException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
 
 /**
@@ -30,8 +25,4 @@
  */
 public class OsmServerUserPreferencesReader extends OsmServerReader {
-
-    protected static String getAttribute(Node node, String name) {
-        return node.getAttributes().getNamedItem(name).getNodeValue();
-    }
 
     /**
@@ -98,17 +89,6 @@
      */
     public Map<String, String> fetchUserPreferences(ProgressMonitor monitor, String reason) throws OsmTransferException {
-        try {
-            monitor.beginTask("");
-            monitor.indeterminateSubTask(tr("Reading user preferences ..."));
-            try (InputStream in = getInputStream("user/preferences", monitor.createSubTaskMonitor(1, true), reason)) {
-                return buildFromXML(Utils.parseSafeDOM(in));
-            }
-        } catch (OsmTransferException e) {
-            throw e;
-        } catch (IOException | ParserConfigurationException | SAXException e) {
-            throw new OsmTransferException(e);
-        } finally {
-            monitor.finishTask();
-        }
+        return fetchData("user/preferences", tr("Reading user preferences ..."),
+                OsmServerUserPreferencesReader::buildFromXML, monitor, reason);
     }
 }
