Index: /trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java	(revision 16610)
+++ /trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadReferrersTask.java	(revision 16611)
@@ -15,4 +15,5 @@
 import javax.swing.SwingUtilities;
 
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSetMerger;
@@ -27,8 +28,10 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.MultiFetchOverpassObjectReader;
 import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
 import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
 import org.openstreetmap.josm.io.OsmServerReader;
 import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.OverpassDownloadReader;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ExceptionUtil;
@@ -155,35 +158,43 @@
     protected void realRun() throws SAXException, IOException, OsmTransferException {
         try {
-            progressMonitor.setTicksCount(children.size());
-            int i = 1;
-            for (PrimitiveId p : children) {
-                if (canceled)
-                    return;
-                String msg;
-                String id = Long.toString(p.getUniqueId());
-                switch(p.getType()) {
-                case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
-                case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
-                case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
-                default: throw new AssertionError();
+            if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) {
+                String request = MultiFetchOverpassObjectReader.genOverpassQuery(children, false, true, false);
+                reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0),
+                        OverpassDownloadReader.OVERPASS_SERVER.get(), request);
+                DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
+                new DataSetMerger(parents, ds).merge();
+            } else {
+                progressMonitor.setTicksCount(children.size());
+                int i = 1;
+                for (PrimitiveId p : children) {
+                    if (canceled)
+                        return;
+                    String msg;
+                    String id = Long.toString(p.getUniqueId());
+                    switch(p.getType()) {
+                    case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break;
+                    case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break;
+                    case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break;
+                    default: throw new AssertionError();
+                    }
+                    progressMonitor.subTask(msg);
+                    downloadParents(p.getUniqueId(), p.getType(), progressMonitor);
+                    i++;
                 }
-                progressMonitor.subTask(msg);
-                downloadParents(p.getUniqueId(), p.getType(), progressMonitor);
-                i++;
-            }
-            Collection<Way> ways = parents.getWays();
-
-            if (!ways.isEmpty()) {
-                // Collect incomplete nodes of parent ways
-                Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete))
-                        .collect(Collectors.toSet());
-                if (!nodes.isEmpty()) {
-                    reader = MultiFetchServerObjectReader.create();
-                    ((MultiFetchServerObjectReader) reader).append(nodes);
-                    DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
-                    synchronized (this) { // avoid race condition in cancel()
-                        reader = null;
+                Collection<Way> ways = parents.getWays();
+
+                if (!ways.isEmpty()) {
+                    // Collect incomplete nodes of parent ways
+                    Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete))
+                            .collect(Collectors.toSet());
+                    if (!nodes.isEmpty()) {
+                        reader = MultiFetchServerObjectReader.create();
+                        ((MultiFetchServerObjectReader) reader).append(nodes);
+                        DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
+                        synchronized (this) { // avoid race condition in cancel()
+                            reader = null;
+                        }
+                        new DataSetMerger(parents, wayNodes).merge();
                     }
-                    new DataSetMerger(parents, wayNodes).merge();
                 }
             }
@@ -194,3 +205,4 @@
         }
     }
+
 }
Index: /trunk/src/org/openstreetmap/josm/gui/io/DownloadFromOverpassTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/DownloadFromOverpassTask.java	(revision 16611)
+++ /trunk/src/org/openstreetmap/josm/gui/io/DownloadFromOverpassTask.java	(revision 16611)
@@ -0,0 +1,70 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSetMerger;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.OverpassDownloadReader;
+import org.xml.sax.SAXException;
+
+/**
+ * Download OSM data from Overpass API
+ *
+ */
+public class DownloadFromOverpassTask extends PleaseWaitRunnable {
+    private boolean canceled;
+    private final String request;
+    private final DataSet ds;
+    private Exception lastException;
+
+    /**
+     * Constructor
+     * @param request the overpass query
+     * @param ds the {@code DataSet} instance that should contain the downloaded data
+     * @param monitor ProgressMonitor to use or null to create a new one.
+     */
+    public DownloadFromOverpassTask(String request, DataSet ds, ProgressMonitor monitor) {
+        super(tr("Download objects via Overpass API"), monitor, false);
+        this.request = request;
+        this.ds = ds;
+    }
+
+    @Override
+    protected void cancel() {
+        canceled = true;
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            OverpassDownloadReader reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0),
+                    OverpassDownloadReader.OVERPASS_SERVER.get(), request);
+            DataSet tmpDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
+            if (!canceled) {
+                new DataSetMerger(ds, tmpDs).merge();
+            }
+        } catch (OsmTransferException e) {
+            if (canceled)
+                return;
+            lastException = e;
+        }
+
+    }
+
+    @Override
+    protected void finish() {
+        if (canceled)
+            return;
+        if (lastException != null) {
+            ExceptionDialogUtil.explainException(lastException);
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesWithReferrersTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesWithReferrersTask.java	(revision 16610)
+++ /trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesWithReferrersTask.java	(revision 16611)
@@ -10,5 +10,7 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -32,5 +34,7 @@
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
+import org.openstreetmap.josm.io.MultiFetchOverpassObjectReader;
 import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.io.OverpassDownloadReader;
 import org.openstreetmap.josm.tools.GBC;
 import org.xml.sax.SAXException;
@@ -51,10 +55,11 @@
     /** Temporary layer where downloaded primitives are put */
     private final OsmDataLayer tmpLayer;
-    /** Reference to the task that download requested primitives */
-    private DownloadPrimitivesTask mainTask;
     /** Flag indicated that user ask for cancel this task */
     private boolean canceled;
     /** Reference to the task currently running */
     private PleaseWaitRunnable currentTask;
+
+    /** set of missing ids, with overpass API these are also deleted objects */
+    private Set<PrimitiveId> missingPrimitives;
 
     /**
@@ -102,7 +107,25 @@
     @Override
     protected void realRun() throws SAXException, IOException, OsmTransferException {
+        if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) {
+            useOverpassApi();
+        } else {
+            useOSMApi();
+        }
+    }
+
+    private void useOverpassApi() {
+        String request = MultiFetchOverpassObjectReader.genOverpassQuery(ids, true, downloadReferrers, full);
+        currentTask = new DownloadFromOverpassTask(request, tmpLayer.data, getProgressMonitor().createSubTaskMonitor(1, false));
+        currentTask.run();
+        missingPrimitives = ids.stream()
+                .filter(id -> tmpLayer.data.getPrimitiveById(id) == null)
+                .collect(Collectors.toSet());
+    }
+
+    private void useOSMApi() {
         getProgressMonitor().setTicksCount(ids.size()+1);
         // First, download primitives
-        mainTask = new DownloadPrimitivesTask(tmpLayer, ids, full, getProgressMonitor().createSubTaskMonitor(1, false));
+        DownloadPrimitivesTask mainTask = new DownloadPrimitivesTask(tmpLayer, ids, full,
+                getProgressMonitor().createSubTaskMonitor(1, false));
         synchronized (this) {
             currentTask = mainTask;
@@ -113,4 +136,7 @@
         }
         currentTask.run();
+
+        missingPrimitives = mainTask.getMissingPrimitives();
+
         // Then, download referrers for each primitive
         if (downloadReferrers && tmpLayer.data != null) {
@@ -144,7 +170,32 @@
             layer.mergeFrom(tmpLayer);
 
+        // Collect known deleted primitives
+        final Set<PrimitiveId> del = new HashSet<>();
+        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
+        for (PrimitiveId id : ids) {
+            OsmPrimitive osm = ds.getPrimitiveById(id);
+            if (osm != null && osm.isDeleted()) {
+                del.add(id);
+            }
+        }
+        final Set<PrimitiveId> errs;
+        if (missingPrimitives != null) {
+            errs = missingPrimitives.stream().filter(id -> !del.contains(id)).collect(Collectors.toCollection(LinkedHashSet::new));
+        } else {
+            errs = Collections.emptySet();
+        }
+
         // Warm about missing primitives
-        final Set<PrimitiveId> errs = mainTask.getMissingPrimitives();
-        if (errs != null && !errs.isEmpty())
+        if (!errs.isEmpty()) {
+            final String assumedApiRC;
+            if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) {
+                assumedApiRC = trn("The server did not return data for the requested object, it was either deleted or does not exist.",
+                        "The server did not return data for the requested objects, they were either deleted or do not exist.",
+                        errs.size());
+
+            } else {
+                assumedApiRC = tr("The server replied with response code 404.<br>"
+                        + "This usually means, the server does not know an object with the requested id.");
+            }
             GuiHelper.runInEDTAndWait(() -> reportProblemDialog(errs,
                     trn("Object could not be downloaded", "Some objects could not be downloaded", errs.size()),
@@ -153,19 +204,11 @@
                             errs.size(),
                             errs.size())
-                            + tr("The server replied with response code 404.<br>"
-                                 + "This usually means, the server does not know an object with the requested id."),
+                            + assumedApiRC,
                     tr("missing objects:"),
                     JOptionPane.ERROR_MESSAGE
                     ).showDialog());
+        }
 
         // Warm about deleted primitives
-        final Set<PrimitiveId> del = new HashSet<>();
-        DataSet ds = MainApplication.getLayerManager().getEditDataSet();
-        for (PrimitiveId id : ids) {
-            OsmPrimitive osm = ds.getPrimitiveById(id);
-            if (osm != null && osm.isDeleted()) {
-                del.add(id);
-            }
-        }
         if (!del.isEmpty())
             GuiHelper.runInEDTAndWait(() -> reportProblemDialog(del,
@@ -191,5 +234,5 @@
         }
         List<PrimitiveId> downloaded = new ArrayList<>(ids);
-        downloaded.removeAll(mainTask.getMissingPrimitives());
+        downloaded.removeAll(missingPrimitives);
         return downloaded;
     }
Index: /trunk/src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java	(revision 16610)
+++ /trunk/src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java	(revision 16611)
@@ -2,9 +2,15 @@
 package org.openstreetmap.josm.io;
 
-import java.util.Map.Entry;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -15,47 +21,99 @@
  */
 public class MultiFetchOverpassObjectReader extends MultiFetchServerObjectReader {
+    private static final List<OsmPrimitiveType> wantedOrder = Arrays.asList(OsmPrimitiveType.RELATION,
+            OsmPrimitiveType.WAY, OsmPrimitiveType.NODE);
 
     private static String getPackageString(final OsmPrimitiveType type, Set<Long> idPackage) {
         return idPackage.stream().map(String::valueOf)
-                .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ");"));
+                .collect(Collectors.joining(",", type.getAPIName() + (idPackage.size() == 1 ? "(" : "(id:"), ")"));
     }
 
     /**
-     * Create a single query for all elements
-     * @return the request string
+     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
+     * children, the objects, or any combination of them.
+     * @param ids the collection of ids
+     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
+     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
+     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
+     * @return the overpass query
+     * @since xxx
      */
-    protected String buildComplexRequestString() {
-        StringBuilder sb = new StringBuilder();
-        int countTypes = 0;
-        for (Entry<OsmPrimitiveType, Set<Long>> e : primitivesMap.entrySet()) {
-            if (!e.getValue().isEmpty()) {
-                countTypes++;
-                String list = getPackageString(e.getKey(), e.getValue());
-                switch (e.getKey()) {
-                case MULTIPOLYGON:
-                case RELATION:
+    public static String genOverpassQuery(Collection<? extends PrimitiveId> ids, boolean includeObjects, boolean recurseUp,
+            boolean recurseDownRelations) {
+        Map<OsmPrimitiveType, Set<Long>> primitivesMap = new LinkedHashMap<>();
+        Arrays.asList(OsmPrimitiveType.RELATION, OsmPrimitiveType.WAY, OsmPrimitiveType.NODE)
+                .forEach(type -> primitivesMap.put(type, new TreeSet<>()));
+        for (PrimitiveId p : ids) {
+            primitivesMap.get(p.getType()).add(p.getUniqueId());
+        }
+        return genOverpassQuery(primitivesMap, includeObjects, recurseUp, recurseDownRelations);
+    }
+
+    /**
+     * Generate single overpass query to retrieve multiple primitives. Can be used to download parents,
+     * children, the objects, or any combination of them.
+     * @param primitivesMap map containing the primitives
+     * @param includeObjects if false, don't retrieve the primitives (e.g. only the referrers)
+     * @param recurseUp if true, referrers (parents) of the objects are downloaded and all nodes of parent ways
+     * @param recurseDownRelations true: yes, recurse down to retrieve complete relations
+     * @return the overpass query
+     */
+    protected static String genOverpassQuery(Map<OsmPrimitiveType, Set<Long>> primitivesMap, boolean includeObjects,
+            boolean recurseUp, boolean recurseDownRelations) {
+        if (!(includeObjects || recurseUp || recurseDownRelations))
+            throw new IllegalArgumentException("At least one options must be true");
+        StringBuilder sb = new StringBuilder(128);
+        StringBuilder setsToInclude = new StringBuilder();
+        StringBuilder up = new StringBuilder();
+        String down = null;
+        for (OsmPrimitiveType type : wantedOrder) {
+            Set<Long> set = primitivesMap.get(type);
+            if (!set.isEmpty()) {
+                sb.append(getPackageString(type, set));
+                if (type == OsmPrimitiveType.NODE) {
+                    sb.append("->.n;");
+                    if (includeObjects) {
+                        setsToInclude.append(".n;");
+                    }
+                    if (recurseUp) {
+                        up.append(".n;way(bn)->.wn;.n;rel(bn)->.rn;");
+                        setsToInclude.append(".wn;node(w);.rn;");
+                    }
+                } else if (type == OsmPrimitiveType.WAY) {
+                    sb.append("->.w;");
+                    if (includeObjects) {
+                        setsToInclude.append(".w;>;");
+                    }
+                    if (recurseUp) {
+                        up.append(".w;rel(bw)->.pw;");
+                        setsToInclude.append(".pw;");
+                    }
+                } else {
+                    sb.append("->.r;");
+                    if (includeObjects) {
+                        setsToInclude.append(".r;");
+                    }
+                    if (recurseUp) {
+                        up.append(".r;rel(br)->.pr;");
+                        setsToInclude.append(".pr;");
+                    }
                     if (recurseDownRelations) {
-                        sb.append('(').append(list);
-                        sb.setLength(sb.length()-1); // remove semicolon
-                        //recurse down only one level, see #18835
-                        sb.append("->.r;.r;rel(r);.r;way(r);>;.r;node(r););");
-                    } else {
-                        sb.append(list);
+                        // get complete ways and nodes of the relation and next level of sub relations
+                        down = ".r;rel(r)->.rm;";
+                        setsToInclude.append(".r;>;.rm;");
                     }
-                    break;
-                case CLOSEDWAY:
-                case WAY:
-                    sb.append('(').append(list).append(">;);");
-                    break;
-                case NODE:
-                    sb.append(list);
                 }
             }
         }
+        if (up.length() > 0) {
+            sb.append(up);
+        }
+        if (down != null) {
+            sb.append(down);
+        }
+        sb.append('(').append(setsToInclude).append(");");
+
+        sb.append("out meta;");
         String query = sb.toString();
-        if (countTypes > 1) {
-            query = "(" + query + ");";
-        }
-        query += "out meta;";
         Logging.debug("{0} {1}", "Generated Overpass query:", query);
         return query;
Index: /trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 16610)
+++ /trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 16611)
@@ -383,9 +383,9 @@
             if (this instanceof MultiFetchOverpassObjectReader) {
                 // calculate a single request for all the objects
-                String request = ((MultiFetchOverpassObjectReader) this).buildComplexRequestString();
+                String request = MultiFetchOverpassObjectReader.genOverpassQuery(primitivesMap, true, false, recurseDownRelations);
                 if (isCanceled())
                     return null;
                 OverpassDownloadReader reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0), getBaseUrl(), request);
-                DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(n, false));
+                DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
                 new DataSetMerger(outputDataSet, ds).merge();
                 checkMissing(outputDataSet, progressMonitor);
Index: /trunk/test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java	(revision 16610)
+++ /trunk/test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java	(revision 16611)
@@ -5,8 +5,10 @@
 
 import java.util.Arrays;
+import java.util.List;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
@@ -31,9 +33,51 @@
      */
     @Test
+    public void testBuildRequestNodesString() {
+        List<OsmPrimitive> objects = Arrays.asList(new Node(123), new Node(126), new Node(130));
+        String requestString;
+        // nodes without parents
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, false);
+        assertEquals("node(id:123,126,130)->.n;(.n;);out meta;", requestString);
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, true);
+        assertEquals("node(id:123,126,130)->.n;(.n;);out meta;", requestString);
+
+        // nodes with parents
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, false);
+        assertEquals("node(id:123,126,130)->.n;.n;way(bn)->.wn;.n;rel(bn)->.rn;(.n;.wn;node(w);.rn;);out meta;",
+                requestString);
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, true);
+        assertEquals("node(id:123,126,130)->.n;.n;way(bn)->.wn;.n;rel(bn)->.rn;(.n;.wn;node(w);.rn;);out meta;",
+                requestString);
+
+        // simulate download referrers
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, false, true, false);
+        assertEquals("node(id:123,126,130)->.n;.n;way(bn)->.wn;.n;rel(bn)->.rn;(.wn;node(w);.rn;);out meta;",
+                requestString);
+
+    }
+
+    /**
+     * Test {@link MultiFetchOverpassObjectReader#buildRequestString}
+     */
+    @Test
     public void testBuildRequestWaysString() {
-        MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader();
-        reader.append(Arrays.asList(new Way(123), new Way(126), new Way(130)));
-        String requestString = reader.buildComplexRequestString();
-        assertEquals("(way(id:123,126,130);>;);out meta;", requestString);
+        List<OsmPrimitive> objects = Arrays.asList(new Way(123), new Way(126), new Way(130));
+        String requestString;
+        // ways without parents (always with nodes)
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, false);
+        assertEquals("way(id:123,126,130)->.w;(.w;>;);out meta;", requestString);
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, true);
+        assertEquals("way(id:123,126,130)->.w;(.w;>;);out meta;", requestString);
+
+        // ways with parents (always with nodes)
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, false);
+        assertEquals("way(id:123,126,130)->.w;.w;rel(bw)->.pw;(.w;>;.pw;);out meta;", requestString);
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, true);
+        assertEquals("way(id:123,126,130)->.w;.w;rel(bw)->.pw;(.w;>;.pw;);out meta;", requestString);
+
+        // simulate download referrers
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, false, true, false);
+        assertEquals("way(id:123,126,130)->.w;.w;rel(bw)->.pw;(.pw;);out meta;", requestString);
+
     }
 
@@ -43,12 +87,23 @@
     @Test
     public void testBuildRequestRelationsString() {
-        MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader();
-        reader.append(Arrays.asList(new Relation(123), new Relation(126), new Relation(130)));
-        reader.setRecurseDownRelations(true);
-        String requestString = reader.buildComplexRequestString();
-        assertEquals("(relation(id:123,126,130)->.r;.r;rel(r);.r;way(r);>;.r;node(r););out meta;", requestString);
-        reader.setRecurseDownRelations(false);
-        requestString = reader.buildComplexRequestString();
-        assertEquals("relation(id:123,126,130);out meta;", requestString);
+        List<OsmPrimitive> objects = Arrays.asList(new Relation(123), new Relation(126), new Relation(130));
+        String requestString;
+        // objects without parents or children
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, false);
+        assertEquals("relation(id:123,126,130)->.r;(.r;);out meta;", requestString);
+        // objects without parents, with children
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, true);
+        assertEquals("relation(id:123,126,130)->.r;.r;rel(r)->.rm;(.r;.r;>;.rm;);out meta;", requestString);
+        // objects with parents, without children
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, false);
+        assertEquals("relation(id:123,126,130)->.r;.r;rel(br)->.pr;(.r;.pr;);out meta;", requestString);
+        // objects with parents and with children
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, true);
+        assertEquals("relation(id:123,126,130)->.r;.r;rel(br)->.pr;.r;rel(r)->.rm;(.r;.pr;.r;>;.rm;);out meta;",
+                requestString);
+        // simulate download referrers
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, false, true, false);
+        assertEquals("relation(id:123,126,130)->.r;.r;rel(br)->.pr;(.pr;);out meta;", requestString);
+
     }
 
@@ -58,16 +113,32 @@
     @Test
     public void testBuildComplexString() {
-        MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader();
-        reader.setRecurseDownRelations(true);
-        reader.append(Arrays.asList(new Relation(123), new Relation(126), new Relation(130), new Way(88), new Way(99),
-                new Node(1)));
-        String requestString = reader.buildComplexRequestString();
+        List<OsmPrimitive> objects = Arrays.asList(new Relation(123), new Relation(126), new Relation(130), new Way(88), new Way(99),
+                new Node(1));
+        // all request strings should start with the same list of objects
+        final String ids =  "relation(id:123,126,130)->.r;way(id:88,99)->.w;node(1)->.n;";
+        String requestString;
+
+        // objects without parents (ways always with nodes)
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, false);
+        assertEquals(ids + "(.r;.w;>;.n;);out meta;", requestString);
+        // objects without parents (ways always with nodes), recurse down one level for sub relations
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, false, true);
+        assertEquals(ids + ".r;rel(r)->.rm;(.r;.r;>;.rm;.w;>;.n;);out meta;", requestString);
+
+        // objects with parents
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, false);
         assertEquals(
-                "((relation(id:123,126,130)->.r;.r;rel(r);.r;way(r);>;.r;node(r););(way(id:88,99);>;);node(1););out meta;",
+                ids + ".r;rel(br)->.pr;.w;rel(bw)->.pw;.n;way(bn)->.wn;.n;rel(bn)->.rn;(.r;.pr;.w;>;.pw;.n;.wn;node(w);.rn;);out meta;",
                 requestString);
-        reader.setRecurseDownRelations(false);
-        requestString = reader.buildComplexRequestString();
-        assertEquals("(relation(id:123,126,130);(way(id:88,99);>;);node(1););out meta;", requestString);
+
+        // objects with parents, recurse down one level for sub relations
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, true, true, true);
+        assertEquals(ids + ".r;rel(br)->.pr;.w;rel(bw)->.pw;.n;way(bn)->.wn;.n;rel(bn)->.rn;.r;rel(r)->.rm;"
+                + "(.r;.pr;.r;>;.rm;.w;>;.pw;.n;.wn;node(w);.rn;);out meta;", requestString);
+        // simulate download referrers
+        requestString = MultiFetchOverpassObjectReader.genOverpassQuery(objects, false, true, false);
+        assertEquals(
+                ids + ".r;rel(br)->.pr;.w;rel(bw)->.pw;.n;way(bn)->.wn;.n;rel(bn)->.rn;(.pr;.pw;.wn;node(w);.rn;);out meta;",
+                requestString);
     }
-
 }
