Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1805)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1806)
@@ -31,8 +31,10 @@
 import javax.swing.JScrollPane;
 import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
 import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -87,4 +89,5 @@
     private AutoCompletionCache acCache;
     private AutoCompletionList acList;
+    private ReferringRelationsBrowserModel referrerModel;
 
     /** the member table */
@@ -121,4 +124,5 @@
         memberTableModel = new MemberTableModel();
         selectionTableModel = new SelectionTableModel(getLayer());
+        referrerModel = new ReferringRelationsBrowserModel(relation);
 
         // populate the models
@@ -143,5 +147,11 @@
 
         getContentPane().setLayout(new BorderLayout());
-        getContentPane().add(pnl,BorderLayout.CENTER);
+        JTabbedPane tabbedPane = new JTabbedPane();
+        tabbedPane.add(tr("Tags and Members"), pnl);
+        if (relation != null && relation.id > 0) {
+            tabbedPane.add(tr("Parent Relations"), new ReferringRelationsBrowser(getLayer(), referrerModel));
+        }
+
+        getContentPane().add(tabbedPane,BorderLayout.CENTER);
         getContentPane().add(buildOkCancelButtonPanel(), BorderLayout.SOUTH);
 
@@ -381,8 +391,6 @@
         JPanel pnl = new JPanel();
         pnl.setLayout(new BorderLayout());
-
-        JTable tbl = new JTable(selectionTableModel,new SelectionTableColumnModel());
+        JTable tbl = new JTable(selectionTableModel,new SelectionTableColumnModel(memberTableModel));
         tbl.setEnabled(false);
-
         JScrollPane pane = new JScrollPane(tbl);
         pnl.add(pane, BorderLayout.CENTER);
@@ -533,4 +541,9 @@
         //--- copy relation action
         buttonPanel.add(new SideButton(new DuplicateRelationAction()));
+
+        // -- edit action
+        EditAction editAction = new EditAction();
+        memberTableModel.getSelectionModel().addListSelectionListener(editAction);
+        buttonPanel.add(new SideButton(editAction));
         return buttonPanel;
     }
@@ -966,17 +979,45 @@
     }
 
+    /**
+     * Action for editing the currently selected relation
+     * 
+     *
+     */
+    class EditAction extends AbstractAction implements ListSelectionListener {
+        public EditAction() {
+            putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
+            putValue(NAME, tr("Edit"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(memberTable.getSelectedRowCount() == 1 && memberTableModel.isEditableRelation(memberTable.getSelectedRow()));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            int idx = memberTable.getSelectedRow();
+            if (idx < 0) return;
+            OsmPrimitive primitive = memberTableModel.getReferredPrimitive(idx);
+            if (! (primitive instanceof Relation)) return;
+            Relation r= (Relation)primitive;
+            if (r.incomplete) return;
+            RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
+            editor.setVisible(true);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    /**
+     * The asynchronous task for downloading relation members.
+     * 
+     *
+     */
     class DownloadTask extends PleaseWaitRunnable {
         private boolean cancelled;
         private Exception lastException;
-
-        protected void setIndeterminateEnabled(final boolean enabled) {
-            EventQueue.invokeLater(
-                    new Runnable() {
-                        public void run() {
-                            Main.pleaseWaitDlg.setIndeterminate(enabled);
-                        }
-                    }
-            );
-        }
 
         public DownloadTask() {
@@ -1013,7 +1054,13 @@
         protected void realRun() throws SAXException, IOException, OsmTransferException {
             try {
-                Main.pleaseWaitDlg.setAlwaysOnTop(true);
-                Main.pleaseWaitDlg.toFront();
-                setIndeterminateEnabled(true);
+                SwingUtilities.invokeLater(
+                        new Runnable() {
+                            public void run() {
+                                Main.pleaseWaitDlg.setAlwaysOnTop(true);
+                                Main.pleaseWaitDlg.toFront();
+                                Main.pleaseWaitDlg.setIndeterminate(true);
+                            }
+                        }
+                );
                 OsmServerObjectReader reader = new OsmServerObjectReader(getRelation().id, OsmPrimitiveType.RELATION, true);
                 DataSet dataSet = reader.parseOsm();
@@ -1026,6 +1073,15 @@
                         getLayer().data.dataSources.add(src);
                     }
-                    getLayer().fireDataChange();
-
+                    // FIXME: this is necessary because there are  dialogs listening
+                    // for DataChangeEvents which manipulate Swing components on this
+                    // thread.
+                    //
+                    SwingUtilities.invokeLater(
+                            new Runnable() {
+                                public void run() {
+                                    getLayer().fireDataChange();
+                                }
+                            }
+                    );
                     if (visitor.getConflicts().isEmpty())
                         return;
@@ -1049,6 +1105,12 @@
                 lastException = e;
             } finally {
-                Main.pleaseWaitDlg.setAlwaysOnTop(false);
-                setIndeterminateEnabled(false);
+                SwingUtilities.invokeLater(
+                        new Runnable() {
+                            public void run() {
+                                Main.pleaseWaitDlg.setAlwaysOnTop(false);
+                                Main.pleaseWaitDlg.setIndeterminate(false);
+                            }
+                        }
+                );
             }
         }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 1805)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java	(revision 1806)
@@ -379,3 +379,11 @@
         }
     }
+
+    public boolean isEditableRelation(int row) {
+        if (row < 0 || row >= members.size()) return false;
+        RelationMember member = members.get(row);
+        if (!(member.member instanceof Relation)) return false;
+        Relation r = (Relation)member.member;
+        return ! r.incomplete;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java	(revision 1806)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java	(revision 1806)
@@ -0,0 +1,290 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.EventQueue;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSource;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
+import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.xml.sax.SAXException;
+
+/**
+ * This is browser for a list of relations which refer to another relations
+ * 
+ *
+ */
+public class ReferringRelationsBrowser extends JPanel {
+
+    /** the list of relations */
+    private JList referrers;
+    private ReferringRelationsBrowserModel model;
+    private OsmDataLayer layer;
+    private JCheckBox cbReadFull;
+    private EditAction editAction;
+
+    /**
+     * build the GUI
+     */
+    protected void build() {
+        setLayout(new BorderLayout());
+        referrers = new JList(model);
+        referrers.setCellRenderer(new OsmPrimitivRenderer());
+        add(referrers, BorderLayout.CENTER);
+        referrers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        referrers.addMouseListener(new DblClickMouseAdapter());
+
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new FlowLayout(FlowLayout.LEFT));
+
+        ReloadAction reloadAction = new ReloadAction();
+        referrers.getModel().addListDataListener(reloadAction);
+        pnl.add(new SideButton(reloadAction));
+        pnl.add(new JLabel(tr("including immediate children of parent relations")));
+        pnl.add(cbReadFull = new JCheckBox());
+
+        editAction = new EditAction();
+        referrers.getSelectionModel().addListSelectionListener(editAction);
+        pnl.add(new SideButton(editAction));
+        add(pnl, BorderLayout.SOUTH);
+    }
+
+    public ReferringRelationsBrowser(OsmDataLayer layer, ReferringRelationsBrowserModel model) {
+        this.model = model;
+        this.layer = layer;
+        build();
+    }
+
+    protected OsmDataLayer getLayer() {
+        return layer;
+    }
+
+    /**
+     * Action for loading the parent relations of a relation
+     *
+     */
+    class ReloadAction extends AbstractAction implements ListDataListener {
+        public ReloadAction() {
+            putValue(SHORT_DESCRIPTION, tr("Load parent relations"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
+            putValue(NAME, tr("Reload"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(model.canReload());
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            boolean full = cbReadFull.isSelected();
+            ReloadTask task = new ReloadTask(full);
+            Main.worker.submit(task);
+        }
+
+        public void contentsChanged(ListDataEvent e) {
+            refreshEnabled();
+        }
+
+        public void intervalAdded(ListDataEvent e) {
+            refreshEnabled();
+        }
+
+        public void intervalRemoved(ListDataEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    /**
+     * Action for editing the currently selected relation
+     * 
+     */
+    class EditAction extends AbstractAction implements ListSelectionListener {
+        public EditAction() {
+            putValue(SHORT_DESCRIPTION, tr("Edit the currently selected relation"));
+            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
+            putValue(NAME, tr("Edit"));
+            refreshEnabled();
+        }
+
+        protected void refreshEnabled() {
+            setEnabled(referrers.getSelectionModel().getMinSelectionIndex() >=0);
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            run();
+        }
+
+        public void run() {
+            int idx = referrers.getSelectedIndex();
+            if (idx < 0) return;
+            Relation r = model.get(idx);
+            if (r == null) return;
+            RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
+            editor.setVisible(true);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            refreshEnabled();
+        }
+    }
+
+    class DblClickMouseAdapter extends MouseAdapter {
+        @Override
+        public void mouseClicked(MouseEvent e) {
+            if (e.getClickCount() == 2)  {
+                editAction.run();
+            }
+        }
+    }
+
+    /**
+     * Asynchronous task for loading the parent relations
+     *
+     */
+    class ReloadTask extends PleaseWaitRunnable {
+        private boolean cancelled;
+        private Exception lastException;
+        private DataSet referrers;
+        private boolean full;
+
+        protected void setIndeterminateEnabled(final boolean enabled) {
+            EventQueue.invokeLater(
+                    new Runnable() {
+                        public void run() {
+                            Main.pleaseWaitDlg.setIndeterminate(enabled);
+                        }
+                    }
+            );
+        }
+
+        public ReloadTask(boolean full) {
+            super(tr("Download referring relations"), false /* don't ignore exception */);
+            referrers = null;
+        }
+        @Override
+        protected void cancel() {
+            cancelled = true;
+            OsmApi.getOsmApi().cancel();
+        }
+
+        protected void showLastException() {
+            String msg = lastException.getMessage();
+            if (msg == null) {
+                msg = lastException.toString();
+            }
+            JOptionPane.showMessageDialog(
+                    null,
+                    msg,
+                    tr("Error"),
+                    JOptionPane.ERROR_MESSAGE
+            );
+        }
+
+        @Override
+        protected void finish() {
+            if (cancelled) return;
+            if (lastException != null) {
+                showLastException();
+                return;
+            }
+            final ArrayList<Relation> parents = new ArrayList<Relation>();
+            for (Relation parent : referrers.relations) {
+                parents.add((Relation)getLayer().data.getPrimitiveById(parent.id));
+            }
+            SwingUtilities.invokeLater(
+                    new Runnable() {
+                        public void run() {
+                            model.populate(parents);
+                        }
+                    }
+            );
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                Main.pleaseWaitDlg.setAlwaysOnTop(true);
+                Main.pleaseWaitDlg.toFront();
+                setIndeterminateEnabled(true);
+                OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(model.getRelation(), full);
+                referrers = reader.parseOsm();
+                if (referrers != null) {
+                    final MergeVisitor visitor = new MergeVisitor(getLayer().data, referrers);
+                    visitor.merge();
+
+                    // copy the merged layer's data source info
+                    for (DataSource src : referrers.dataSources) {
+                        getLayer().data.dataSources.add(src);
+                    }
+                    // FIXME: this is necessary because there are  dialogs listening
+                    // for DataChangeEvents which manipulate Swing components on this
+                    // thread.
+                    //
+                    SwingUtilities.invokeLater(
+                            new Runnable() {
+                                public void run() {
+                                    getLayer().fireDataChange();
+                                }
+                            }
+                    );
+
+                    if (visitor.getConflicts().isEmpty())
+                        return;
+                    getLayer().getConflicts().add(visitor.getConflicts());
+                    JOptionPane op = new JOptionPane(
+                            tr("There were {0} conflicts during import.",
+                                    visitor.getConflicts().size()),
+                                    JOptionPane.WARNING_MESSAGE
+                    );
+                    JDialog dialog = op.createDialog(Main.pleaseWaitDlg, tr("Conflicts in data"));
+                    dialog.setAlwaysOnTop(true);
+                    dialog.setModal(true);
+                    dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                    dialog.setVisible(true);
+                }
+            } catch(Exception e) {
+                if (cancelled) {
+                    System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e.toString()));
+                    return;
+                }
+                lastException = e;
+            } finally {
+                Main.pleaseWaitDlg.setAlwaysOnTop(false);
+                setIndeterminateEnabled(false);
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java	(revision 1806)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java	(revision 1806)
@@ -0,0 +1,80 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.util.ArrayList;
+
+import javax.swing.AbstractListModel;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+
+public class ReferringRelationsBrowserModel extends AbstractListModel {
+
+    /** the relation */
+    private Relation relation;
+    private ArrayList<Relation> referrers;
+
+    public ReferringRelationsBrowserModel() {
+        relation = null;
+        referrers = new ArrayList<Relation>();
+    }
+    public ReferringRelationsBrowserModel(Relation relation) {
+        this();
+        this.relation = relation;
+    }
+
+    protected void fireModelUpdate() {
+        int upper = Math.max(0, referrers.size() -1);
+        fireContentsChanged(this, 0, upper);
+    }
+
+    public void setRelation(Relation relation) {
+        this.relation = relation;
+        referrers.clear();
+        fireModelUpdate();
+    }
+
+    public Object getElementAt(int index) {
+        return referrers.get(index);
+    }
+
+    public int getSize() {
+        return referrers.size();
+    }
+
+    protected boolean isReferringRelation(Relation parent) {
+        if (parent == null) return false;
+        for (RelationMember m: parent.members) {
+            if (m.member instanceof Relation) {
+                Relation child = (Relation)m.member;
+                if (child.equals(relation)) return true;
+            }
+        }
+        return false;
+    }
+
+    public void populate(ArrayList<Relation> parents) {
+        referrers.clear();
+        if (parents != null) {
+            for (Relation relation: parents) {
+                if (isReferringRelation(relation)) {
+                    referrers.add(relation);
+                }
+            }
+        }
+        fireModelUpdate();
+    }
+
+    public boolean canReload() {
+        return relation != null && relation.id > 0;
+    }
+
+    public Relation getRelation() {
+        return relation;
+    }
+
+    public Relation get(int index) {
+        return referrers.get(index);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java	(revision 1806)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java	(revision 1806)
@@ -0,0 +1,137 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.relation;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * This is the {@see TableCellRenderer} used in the tables of {@see RelationMemberMerger}.
+ * 
+ */
+public  class SelectionTableCellRenderer extends JLabel implements TableCellRenderer {
+    public final static Color BGCOLOR_SELECTED = new Color(143,170,255);
+    public final static Color BGCOLOR_DOUBLE_ENTRY = new Color(255,234,213);
+
+
+    private HashMap<OsmPrimitiveType, ImageIcon>  icons;
+    /**
+     * reference to the member table model; required, in order to check whether a
+     * selected primitive is already used in the member list of the currently edited
+     * relation
+     */
+    private MemberTableModel model;
+
+    /**
+     * Load the image icon for an OSM primitive of type node
+     * 
+     * @return the icon; null, if not found
+     */
+    protected void loadIcons() {
+        icons = new HashMap<OsmPrimitiveType, ImageIcon>();
+        icons.put(OsmPrimitiveType.NODE,ImageProvider.get("data", "node"));
+        icons.put(OsmPrimitiveType.WAY, ImageProvider.get("data", "way"));
+        icons.put(OsmPrimitiveType.RELATION, ImageProvider.get("data", "relation"));
+    }
+
+    /**
+     * constructor
+     */
+    public SelectionTableCellRenderer() {
+        setIcon(null);
+        setOpaque(true);
+        loadIcons();
+    }
+
+    public String buildToolTipText(OsmPrimitive primitive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        sb.append("<strong>id</strong>=")
+        .append(primitive.id)
+        .append("<br>");
+        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
+        Collections.sort(keyList);
+        for (int i = 0; i < keyList.size(); i++) {
+            if (i > 0) {
+                sb.append("<br>");
+            }
+            String key = keyList.get(i);
+            sb.append("<strong>")
+            .append(key)
+            .append("</strong>")
+            .append("=");
+            String value = primitive.get(key);
+            while(value.length() != 0) {
+                sb.append(value.substring(0,Math.min(50, value.length())));
+                if (value.length() > 50) {
+                    sb.append("<br>");
+                    value = value.substring(50);
+                } else {
+                    value = "";
+                }
+            }
+        }
+        sb.append("</html>");
+        return sb.toString();
+    }
+
+    /**
+     * reset the renderer
+     */
+    protected void reset() {
+        setBackground(Color.WHITE);
+        setForeground(Color.BLACK);
+        setBorder(null);
+        setIcon(null);
+        setToolTipText(null);
+    }
+
+    protected void renderBackground(OsmPrimitive primitive, boolean isSelected) {
+        Color bgc = Color.WHITE;
+        if (isSelected) {
+            bgc = BGCOLOR_SELECTED;
+        } else if (primitive != null && model != null && model.getNumMembersWithPrimitive(primitive) > 0) {
+            bgc = BGCOLOR_DOUBLE_ENTRY;
+        }
+        setBackground(bgc);
+    }
+
+    protected void renderForeground(boolean isSelected) {
+        Color fgc = Color.BLACK;
+        setForeground(fgc);
+    }
+
+    protected void renderPrimitive(OsmPrimitive primitive) {
+        NameVisitor visitor = new NameVisitor();
+        primitive.visit(visitor);
+        setIcon(icons.get(OsmPrimitiveType.from(primitive)));
+        setText(visitor.name);
+        setToolTipText(buildToolTipText(primitive));
+    }
+
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+            int row, int column) {
+
+        reset();
+        renderForeground(isSelected);
+        renderBackground((OsmPrimitive)value, isSelected);
+        renderPrimitive((OsmPrimitive)value);
+        return this;
+    }
+
+    public void setMemberTableModel(MemberTableModel model) {
+        this.model = model;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableColumnModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableColumnModel.java	(revision 1805)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableColumnModel.java	(revision 1806)
@@ -7,10 +7,9 @@
 import javax.swing.table.TableColumn;
 
-import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
-
 public class SelectionTableColumnModel  extends DefaultTableColumnModel {
-    public SelectionTableColumnModel() {
+    public SelectionTableColumnModel(MemberTableModel model) {
         TableColumn col = null;
-        OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
+        SelectionTableCellRenderer renderer = new SelectionTableCellRenderer();
+        renderer.setMemberTableModel(model);
 
         // column 0 - the member role
Index: trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1805)
+++ trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1806)
@@ -11,8 +11,6 @@
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
 import java.util.logging.Logger;
@@ -352,4 +350,5 @@
                 w.incomplete = true;
                 w.nodes.clear();
+                adder.visit(w);
             } else {
                 e.getKey().copyTo(w);
Index: trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java	(revision 1806)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java	(revision 1806)
@@ -0,0 +1,265 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
+
+/**
+ * OsmServerBackreferenceReader fetches the primitives from the OSM server which
+ * refer to a specific primitive. For a {@see Node}, ways and relations are retrieved
+ * which refer to the node. For a {@see Way} or a {@see Relation}, only relations are
+ * read.
+ * 
+ * OsmServerBackreferenceReader uses the API calls <code>[node|way|relation]/#id/relations</code>
+ * and  <code>node/#id/ways</code> to retrieve the referring primitives. The default behaviour
+ * of these calls is to reply incomplete primitives only.
+ * 
+ * If you set {@see #setReadFull(boolean)} to true this reader uses a {@see MultiFetchServerObjectReader}
+ * to complete incomplete primitives.
+ * 
+ *
+ */
+public class OsmServerBackreferenceReader extends OsmServerReader {
+
+    /** the id of the primitive whose referrers are to be read */
+    private long id;
+    /** the type of the primitive */
+    private OsmPrimitiveType primitiveType;
+    /** true if this reader should complete incomplete primitives */
+    private boolean readFull;
+
+    /**
+     * constructor
+     * 
+     * @param primitive  the primitive to be read. Must not be null. primitive.id > 0 expected
+     * 
+     * @exception IllegalArgumentException thrown if primitive is null
+     * @exception IllegalArgumentException thrown if primitive.id <= 0
+     */
+    public OsmServerBackreferenceReader(OsmPrimitive primitive) throws IllegalArgumentException {
+        if (primitive == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "primitive"));
+        if (primitive.id == 0)
+            throw new IllegalArgumentException(tr("id parameter ''{0}'' > 0 required. Got {1}", "primitive", primitive.id));
+        this.id = primitive.id;
+        this.primitiveType = OsmPrimitiveType.from(primitive);
+        this.readFull = false;
+    }
+
+    /**
+     * constructor
+     * 
+     * @param id  the id of the primitive. > 0 expected
+     * @param type the type of the primitive. Must not be null.
+     * 
+     * @exception IllegalArgumentException thrown if id <= 0
+     * @exception IllegalArgumentException thrown if type is null
+     * 
+     */
+    public OsmServerBackreferenceReader(long id, OsmPrimitiveType type) throws IllegalArgumentException   {
+        if (id <= 0)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' > 0 required. Got {1}", "id", id));
+        if (type == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "type"));
+        this.id = id;
+        this.primitiveType = type;
+        this.readFull = false;
+    }
+
+    /**
+     * constructor
+     * 
+     * @param id  the id of the primitive. > 0 expected
+     * @param type the type of the primitive. Must not be null.
+     * @param readFull true, if referers should be read fully (i.e. including their immediate children)
+     * 
+     */
+    public OsmServerBackreferenceReader(OsmPrimitive primitive, boolean readFull) {
+        this(primitive);
+        this.readFull = readFull;
+    }
+
+    /**
+     * constructor
+     * 
+     * @param primitive the primitive whose referers are to be read
+     * @param readFull true, if referers should be read fully (i.e. including their immediate children)
+     * 
+     * @exception IllegalArgumentException thrown if id <= 0
+     * @exception IllegalArgumentException thrown if type is null
+     * 
+     */
+    public OsmServerBackreferenceReader(long id, OsmPrimitiveType type, boolean readFull) throws IllegalArgumentException  {
+        this(id, type);
+        this.readFull = false;
+    }
+
+    /**
+     * Replies true if this reader also reads immediate children of referring primitives
+     * 
+     * @return true if this reader also reads immediate children of referring primitives
+     */
+    public boolean isReadFull() {
+        return readFull;
+    }
+
+    /**
+     * Set true if this reader should reads immediate children of referring primitives too. False, otherweise.
+     * 
+     * @param readFull true if this reader should reads immediate children of referring primitives too. False, otherweise.
+     */
+    public void setReadFull(boolean readFull) {
+        this.readFull = readFull;
+    }
+
+    /**
+     * Reads referring ways from the API server and replies them in a {@see DataSet}
+     * 
+     * @return the data set
+     * @throws OsmTransferException
+     */
+    protected DataSet getReferringWays() throws OsmTransferException {
+        InputStream in = null;
+        try {
+            Main.pleaseWaitDlg.progress.setValue(0);
+            Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
+            StringBuffer sb = new StringBuffer();
+            sb.append(primitiveType.getAPIName())
+            .append("/").append(id).append("/ways");
+
+            in = getInputStream(sb.toString(), Main.pleaseWaitDlg);
+            if (in == null)
+                return null;
+            Main.pleaseWaitDlg.currentAction.setText(tr("Downloading referring ways ..."));
+            return OsmReader.parseDataSet(in,Main.pleaseWaitDlg);
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch (Exception e) {
+            if (cancel)
+                return null;
+            throw new OsmTransferException(e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch(Exception e) {}
+                activeConnection = null;
+            }
+        }
+    }
+    /**
+
+     * Reads referring relations from the API server and replies them in a {@see DataSet}
+     * 
+     * @return the data set
+     * @throws OsmTransferException
+     */
+    protected DataSet getReferringRelations() throws OsmTransferException {
+        InputStream in = null;
+        try {
+            Main.pleaseWaitDlg.progress.setValue(0);
+            Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
+            StringBuffer sb = new StringBuffer();
+            sb.append(primitiveType.getAPIName())
+            .append("/").append(id).append("/relations");
+
+            in = getInputStream(sb.toString(), Main.pleaseWaitDlg);
+            if (in == null)
+                return null;
+            Main.pleaseWaitDlg.currentAction.setText(tr("Downloading referring relations ..."));
+            return OsmReader.parseDataSet(in,Main.pleaseWaitDlg);
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch (Exception e) {
+            if (cancel)
+                return null;
+            throw new OsmTransferException(e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch(Exception e) {}
+                activeConnection = null;
+            }
+        }
+    }
+
+    /**
+     * Scans a dataset for incomplete primitives. Depending on the configuration of this reader
+     * incomplete primitives are read from the server with an individual <tt>/api/0.6/[way,relation]/#id/full</tt>
+     * request.
+     * 
+     * <ul>
+     *   <li>if this reader reads referers for an {@see Node}, referring ways are always
+     *     read individually from the server</li>
+     *   <li>if this reader reads referers for an {@see Way} or a {@see Relation}, referring relations
+     *    are only read fully if {@see #setReadFull(boolean)} is set to true.</li>
+     * </ul>
+     * 
+     * The method replies the modified dataset.
+     * 
+     * @param ds the original dataset
+     * @return the modified dataset
+     * @throws OsmTransferException thrown if an exception occurs.
+     */
+    protected DataSet readIncompletePrimitives(DataSet ds) throws OsmTransferException {
+        Collection<Way> waysToCheck = new ArrayList<Way>(ds.ways);
+        if (isReadFull() ||primitiveType.equals(OsmPrimitiveType.NODE)) {
+            for (Way way: waysToCheck) {
+                if (way.id > 0 && way.incomplete) {
+                    OsmServerObjectReader reader = new OsmServerObjectReader(way.id, OsmPrimitiveType.from(way), true /* read full */);
+                    DataSet wayDs = reader.parseOsm();
+                    MergeVisitor visitor = new MergeVisitor(ds, wayDs);
+                    visitor.merge();
+                }
+            }
+        }
+        if (isReadFull()) {
+            Collection<Relation> relationsToCheck  = new ArrayList<Relation>(ds.relations);
+            for (Relation relation: relationsToCheck) {
+                if (relation.id > 0 && relation.incomplete) {
+                    OsmServerObjectReader reader = new OsmServerObjectReader(relation.id, OsmPrimitiveType.from(relation), true /* read full */);
+                    DataSet wayDs = reader.parseOsm();
+                    MergeVisitor visitor = new MergeVisitor(ds, wayDs);
+                    visitor.merge();
+                }
+            }
+        }
+        return ds;
+    }
+
+    /**
+     * Reads the referring primitives from the OSM server, parses them and
+     * replies them as {@see DataSet}
+     * 
+     * @return the dataset with the referring primitives
+     * @exception OsmTransferException thrown if an error occurs while communicating with the server
+     */
+    @Override
+    public DataSet parseOsm() throws OsmTransferException {
+        DataSet ret = new DataSet();
+        if (primitiveType.equals(OsmPrimitiveType.NODE)) {
+            DataSet ds = getReferringWays();
+            MergeVisitor visitor = new MergeVisitor(ret,ds);
+            visitor.merge();
+            ret = visitor.getMyDataSet();
+        }
+        DataSet ds = getReferringRelations();
+        MergeVisitor visitor = new MergeVisitor(ret,ds);
+        visitor.merge();
+        ret = visitor.getMyDataSet();
+        readIncompletePrimitives(ret);
+        return ret;
+    }
+}
Index: trunk/test/config/test-functional-env.properties
===================================================================
--- trunk/test/config/test-functional-env.properties	(revision 1806)
+++ trunk/test/config/test-functional-env.properties	(revision 1806)
@@ -0,0 +1,19 @@
+#
+# This file includes properties which are used by all JOSM functional tests 
+#
+
+#### 
+# josm.home - the home directory of the JOSM installation to be used in functional tests
+#
+# This is the home directory for JOSM preferences: ${josm.home}\preferences
+# This is the home directory for JOSM plugins: ${josm.home}\plugins\*.jar
+#
+josm.home=C:\\data\\projekte\\osm\\tag-editor-plugin
+
+
+####
+# test.functional.tempdir - the directory in which functional test case can save
+#   temporary results 
+#
+test.functional.tempdir=C:\\data\\projekte\\eclipse-3.4.1-ws\\JOSM-1769\\test\\data\\temp
+
Index: trunk/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java
===================================================================
--- trunk/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java	(revision 1805)
+++ trunk/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java	(revision 1806)
@@ -258,5 +258,4 @@
         }
         assertTrue(reader.getMissingPrimitives().isEmpty());
-        //assertTrue(reader.getSkippedWays().isEmpty());
     }
 
@@ -279,6 +278,4 @@
         }
         assertTrue(reader.getMissingPrimitives().isEmpty());
-        //assertTrue(reader.getSkippedWays().isEmpty());
-
     }
 
@@ -301,6 +298,4 @@
         }
         assertTrue(reader.getMissingPrimitives().isEmpty());
-        //assertTrue(reader.getSkippedWays().isEmpty());
-
     }
 
@@ -322,7 +317,4 @@
         }
         assertTrue(reader.getMissingPrimitives().isEmpty());
-        //assertTrue(reader.getSkippedWays().isEmpty());
-
-
     }
 
@@ -348,5 +340,4 @@
         assertEquals(1, reader.getMissingPrimitives().size());
         assertEquals(9999999, reader.getMissingPrimitives().iterator().next());
-        //assertTrue(reader.getSkippedWays().isEmpty());
     }
 }
Index: trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java
===================================================================
--- trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java	(revision 1806)
+++ trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java	(revision 1806)
@@ -0,0 +1,517 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+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.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.projection.Mercator;
+import org.openstreetmap.josm.gui.PleaseWaitDialog;
+import org.xml.sax.SAXException;
+
+public class OsmServerBackreferenceReaderTest {
+    static private final Logger logger = Logger.getLogger(OsmServerBackreferenceReader.class.getName());
+
+    protected static Node lookupNode(DataSet ds, int i) {
+        for (Node n: ds.nodes) {
+            if (("node-" + i).equals(n.get("name"))) return n;
+        }
+        return null;
+    }
+
+
+    protected static Way lookupWay(DataSet ds, int i) {
+        for (Way w: ds.ways) {
+            if (("way-" + i).equals(w.get("name"))) return w;
+        }
+        return null;
+    }
+
+    protected static Relation lookupRelation(DataSet ds, int i) {
+        for (Relation r: ds.relations) {
+            if (("relation-" + i).equals(r.get("name"))) return r;
+        }
+        return null;
+    }
+
+    protected static void populateTestDataSetWithNodes(DataSet ds) {
+        for (int i=0;i<100;i++) {
+            Node n = new Node(0);
+            n.setCoor(new LatLon(-36.6,47.6));
+            n.put("name", "node-"+i);
+            n.incomplete = false;
+            ds.addPrimitive(n);
+        }
+    }
+
+    protected static void populateTestDataSetWithWays(DataSet ds) {
+        for (int i=0;i<20;i++) {
+            Way w = new Way(0);
+            w.incomplete = false;
+            for (int j = 0; j < 10;j++) {
+                w.nodes.add(lookupNode(ds, i+j));
+            }
+            w.put("name", "way-"+i);
+            ds.addPrimitive(w);
+        }
+    }
+
+    protected static void populateTestDataSetWithRelations(DataSet ds) {
+        for (int i=0;i<10;i++) {
+            Relation r = new Relation(0);
+            r.incomplete = false;
+            r.put("name", "relation-" +i);
+            for (int j =0; j < 10; j++) {
+                RelationMember member = new RelationMember();
+                member.role = "node-" + j;
+                member.member = lookupNode(ds, i + j);
+            }
+            for (int j =0; j < 5; j++) {
+                RelationMember member = new RelationMember();
+                member.role = "way-" + j;
+                member.member = lookupWay(ds, i + j);
+                r.members.add(member);
+            }
+            if (i > 5) {
+                for (int j =0; j < 3; j++) {
+                    RelationMember member = new RelationMember();
+                    member.role = "relation-" + j;
+                    member.member = lookupRelation(ds, j);
+                    logger.info(MessageFormat.format("adding relation {0} to relation {1}", j, i));
+                    r.members.add(member);
+                }
+            }
+            ds.relations.add(r);
+        }
+    }
+
+
+    protected static DataSet buildTestDataSet() {
+        DataSet ds = new DataSet();
+        ds.version = "0.6";
+
+        populateTestDataSetWithNodes(ds);
+        populateTestDataSetWithWays(ds);
+        populateTestDataSetWithRelations(ds);
+        return ds;
+    }
+
+    /**
+     * creates the dataset on the server.
+     * 
+     * @param ds the data set
+     * @throws OsmTransferException
+     */
+    static public void createDataSetOnServer(DataSet ds) throws OsmTransferException {
+        logger.info("creating data set on the server ...");
+        ArrayList<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
+        primitives.addAll(ds.nodes);
+        primitives.addAll(ds.ways);
+        primitives.addAll(ds.relations);
+        OsmServerWriter writer = new OsmServerWriter();
+        writer.uploadOsm("0.6", primitives);
+    }
+
+    static Properties testProperties;
+    static DataSet testDataSet;
+
+    @BeforeClass
+    public static void  init() throws OsmTransferException, InterruptedException{
+        logger.info("initializing ...");
+        testProperties = new Properties();
+
+        // load properties
+        //
+        try {
+            testProperties.load(MultiFetchServerObjectReaderTest.class.getResourceAsStream("/test-functional-env.properties"));
+        } catch(Exception e){
+            logger.log(Level.SEVERE, MessageFormat.format("failed to load property file ''{0}''", "test-functional-env.properties"));
+            fail(MessageFormat.format("failed to load property file ''{0}''", "test-functional-env.properties"));
+        }
+
+        // check josm.home
+        //
+        String josmHome = testProperties.getProperty("josm.home");
+        if (josmHome == null) {
+            fail(MessageFormat.format("property ''{0}'' not set in test environment", "josm.home"));
+        } else {
+            File f = new File(josmHome);
+            if (! f.exists() || ! f.canRead()) {
+                fail(MessageFormat.format("property ''{0}'' points to ''{1}'' which is either not existing or not readable", "josm.home", josmHome));
+            }
+        }
+
+        // check temp output dir
+        //
+        String tempOutputDir = testProperties.getProperty("test.functional.tempdir");
+        if (tempOutputDir == null) {
+            fail(MessageFormat.format("property ''{0}'' not set in test environment", "test.functional.tempdir"));
+        } else {
+            File f = new File(tempOutputDir);
+            if (! f.exists() || ! f.isDirectory() || ! f.canWrite()) {
+                fail(MessageFormat.format("property ''{0}'' points to ''{1}'' which is either not existing, not a directory, or not writeable", "test.functional.tempdir", tempOutputDir));
+            }
+        }
+
+
+        // init preferences
+        //
+        System.setProperty("josm.home", josmHome);
+        Main.pleaseWaitDlg = new PleaseWaitDialog(null);
+        Main.pref.init(false);
+        // don't use atomic upload, the test API server can't cope with large diff uploads
+        //
+        Main.pref.put("osm-server.atomic-upload", false);
+        Main.proj = new Mercator();
+
+        File dataSetCacheOutputFile = new File(tempOutputDir, MultiFetchServerObjectReaderTest.class.getName() + ".dataset");
+
+        // make sure we don't upload to production
+        //
+        String url = OsmApi.getOsmApi().getBaseUrl().toLowerCase().trim();
+        if (url.startsWith("http://www.openstreetmap.org")
+                || url.startsWith("http://api.openstreetmap.org")) {
+            fail(MessageFormat.format("configured url ''{0}'' seems to be a productive url, aborting.", url));
+        }
+
+
+        String p = System.getProperties().getProperty("useCachedDataset");
+        if (p != null && Boolean.parseBoolean(p.trim().toLowerCase())) {
+            logger.info(MessageFormat.format("property ''{0}'' set, using cached dataset", "useCachedDataset"));
+            return;
+        }
+
+        logger.info(MessageFormat.format("property ''{0}'' not set to true, creating test dataset on the server. property is ''{1}''", "useCachedDataset", p));
+
+        // build and upload the test data set
+        //
+        logger.info("creating test data set ....");
+        testDataSet = buildTestDataSet();
+        logger.info("uploading test data set ...");
+        createDataSetOnServer(testDataSet);
+
+        PrintWriter pw = null;
+        try {
+            pw = new PrintWriter(
+                    new FileWriter(dataSetCacheOutputFile)
+            );
+        } catch(IOException e) {
+            fail(MessageFormat.format("failed to open file ''{0}'' for writing", dataSetCacheOutputFile.toString()));
+        }
+        logger.info(MessageFormat.format("caching test data set in ''{0}'' ...", dataSetCacheOutputFile.toString()));
+        OsmWriter w = new OsmWriter(pw, false, testDataSet.version);
+        w.header();
+        w.writeDataSources(testDataSet);
+        w.writeContent(testDataSet);
+        w.footer();
+        w.close();
+        pw.close();
+    }
+
+    private DataSet ds;
+
+    @Before
+    public void setUp() throws IOException, SAXException {
+        File f = new File(testProperties.getProperty("test.functional.tempdir"), MultiFetchServerObjectReaderTest.class.getName() + ".dataset");
+        logger.info(MessageFormat.format("reading cached dataset ''{0}''", f.toString()));
+        ds = new DataSet();
+        FileInputStream fis = new FileInputStream(f);
+        ds = OsmReader.parseDataSet(fis, Main.pleaseWaitDlg);
+        fis.close();
+    }
+
+    @Test
+    public void testBackrefrenceForNode() throws OsmTransferException {
+        Node n = lookupNode(ds, 0);
+        assertNotNull(n);
+        Way w = lookupWay(ds, 0);
+        assertNotNull(w);
+
+        OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(n);
+        reader.setReadFull(false);
+        DataSet referers = reader.parseOsm();
+        assertEquals(10, referers.nodes.size());
+        assertEquals(1, referers.ways.size());
+        assertEquals(0, referers.relations.size());
+        for (Way way: referers.ways) {
+            assertEquals(w.id, way.id);
+            assertEquals(false, way.incomplete);
+        }
+    }
+
+    @Test
+    public void testBackrefrenceForNode_Full() throws OsmTransferException {
+        Node n = lookupNode(ds, 0);
+        assertNotNull(n);
+        Way w = lookupWay(ds, 0);
+        assertNotNull(w);
+
+        OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(n);
+        reader.setReadFull(true);
+        DataSet referers = reader.parseOsm();
+        assertEquals(10, referers.nodes.size());
+        assertEquals(1, referers.ways.size());
+        assertEquals(0, referers.relations.size());
+        for (Way way: referers.ways) {
+            assertEquals(w.id, way.id);
+            assertEquals(false, way.incomplete);
+            assertEquals(10, w.nodes.size());
+        }
+    }
+
+    @Test
+    public void testBackrefrenceForWay() throws OsmTransferException {
+        Way w = lookupWay(ds, 1);
+        assertNotNull(w);
+        // way with name "way-1" is referred to by two relations
+        //
+
+        OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(w);
+        reader.setReadFull(false);
+        DataSet referers = reader.parseOsm();
+        assertEquals(0, referers.nodes.size()); // no nodes loaded
+        assertEquals(6, referers.ways.size());  // 6 ways referred by two relations
+        for (Way w1: referers.ways) {
+            assertEquals(true, w1.incomplete);
+        }
+        assertEquals(2, referers.relations.size());  // two relations referring to w
+
+        Relation r = lookupRelation(referers, 0);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        r = lookupRelation(referers, 1);
+        assertEquals(false, r.incomplete);
+    }
+
+    @Test
+    public void testBackrefrenceForWay_Full() throws OsmTransferException {
+        Way w = lookupWay(ds, 1);
+        assertNotNull(w);
+        // way with name "way-1" is referred to by two relations
+        //
+
+        OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(w);
+        reader.setReadFull(true);
+        DataSet referers = reader.parseOsm();
+        assertEquals(6, referers.ways.size());  // 6 ways referred by two relations
+        for (Way w1: referers.ways) {
+            assertEquals(false, w1.incomplete);
+        }
+        assertEquals(2, referers.relations.size());  // two relations referring to
+        Set<Long> expectedNodeIds = new HashSet<Long>();
+        for (Way way: referers.ways) {
+            Way orig = (Way)ds.getPrimitiveById(way.id);
+            for(Node n: orig.nodes) {
+                expectedNodeIds.add(n.id);
+            }
+        }
+        assertEquals(expectedNodeIds.size(), referers.nodes.size());
+        for (Node n : referers.nodes) {
+            assertEquals(true, expectedNodeIds.contains(n.id));
+        }
+
+        Relation r = lookupRelation(referers, 0);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        r = lookupRelation(referers, 1);
+        assertEquals(false, r.incomplete);
+    }
+
+    @Test
+    public void testBackrefrenceForRelation() throws OsmTransferException {
+        Relation r = lookupRelation(ds,1);
+        assertNotNull(r);
+        // way with name "relation-1" is referred to by four relations:
+        //    relation-6, relation-7, relation-8, relation-9
+        //
+
+        OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(r);
+        reader.setReadFull(false);
+        DataSet referers = reader.parseOsm();
+
+        Set<Long> referringRelationsIds = new HashSet<Long>();
+        r = lookupRelation(referers, 6);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+        r = lookupRelation(referers, 7);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+        r = lookupRelation(referers, 8);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+        r = lookupRelation(referers, 9);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+
+        for (Relation r1: referers.relations) {
+            if (! referringRelationsIds.contains(r1.id)) {
+                assertEquals(true, r1.incomplete);
+            }
+        }
+
+        // make sure we read all ways referred to by parent relations. These
+        // ways are incomplete after reading.
+        //
+        Set<Long> expectedWayIds = new HashSet<Long>();
+        for (RelationMember m : lookupRelation(ds, 6).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (RelationMember m : lookupRelation(ds, 7).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (RelationMember m : lookupRelation(ds, 8).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (RelationMember m : lookupRelation(ds, 9).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+
+        assertEquals(expectedWayIds.size(), referers.ways.size());
+        for (Way w1 : referers.ways) {
+            assertEquals(true, expectedWayIds.contains(w1.id));
+            assertEquals(true, w1.incomplete);
+        }
+
+        // make sure we didn't read any nodes
+        //
+        assertEquals(0, referers.nodes.size());
+    }
+
+    protected Set<Long> getNodeIdsInWay(Way way) {
+        HashSet<Long> ret = new HashSet<Long>();
+        if (way == null)return ret;
+        for (Node n: way.nodes) {
+            ret.add(n.id);
+        }
+        return ret;
+    }
+
+    protected Set<Long> getNodeIdsInRelation(Relation r) {
+        HashSet<Long> ret = new HashSet<Long>();
+        if (r == null) return ret;
+        for (RelationMember m: r.members) {
+            if (m.member instanceof Node) {
+                ret.add(m.member.id);
+            } else if (m.member instanceof Way) {
+                ret.addAll(getNodeIdsInWay((Way)m.member));
+            } else if (m.member instanceof Relation) {
+                ret.addAll(getNodeIdsInRelation((Relation)m.member));
+            }
+        }
+        return ret;
+    }
+
+    @Test
+    public void testBackrefrenceForRelation_Full() throws OsmTransferException {
+        Relation r = lookupRelation(ds,1);
+        assertNotNull(r);
+        // way with name "relation-1" is referred to by four relations:
+        //    relation-6, relation-7, relation-8, relation-9
+        //
+
+        OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(r);
+        reader.setReadFull(true);
+        DataSet referers = reader.parseOsm();
+
+        Set<Long> referringRelationsIds = new HashSet<Long>();
+        r = lookupRelation(referers, 6);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+        r = lookupRelation(referers, 7);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+        r = lookupRelation(referers, 8);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+        r = lookupRelation(referers, 9);
+        assertNotNull(r);
+        assertEquals(false, r.incomplete);
+        referringRelationsIds.add(r.id);
+
+        // all relations are fully loaded
+        //
+        for (Relation r1: referers.relations) {
+            assertEquals(false, r1.incomplete);
+        }
+
+        // make sure we read all ways referred to by parent relations. These
+        // ways are completely read after reading the relations
+        //
+        Set<Long> expectedWayIds = new HashSet<Long>();
+        for (RelationMember m : lookupRelation(ds, 6).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (RelationMember m : lookupRelation(ds, 7).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (RelationMember m : lookupRelation(ds, 8).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (RelationMember m : lookupRelation(ds, 9).members) {
+            if (m.member instanceof Way) {
+                expectedWayIds.add(m.member.id);
+            }
+        }
+        for (long id : expectedWayIds) {
+            Way w = (Way)referers.getPrimitiveById(id);
+            assertNotNull(w);
+            assertEquals(false, w.incomplete);
+        }
+
+        Set<Long> expectedNodeIds = new HashSet<Long>();
+        for(int i=6; i< 10;i++) {
+            Relation r1 = lookupRelation(ds, i);
+            expectedNodeIds.addAll(getNodeIdsInRelation(r1));
+        }
+
+        assertEquals(expectedNodeIds.size(), referers.nodes.size());
+        for(Node n : referers.nodes) {
+            assertEquals(true, expectedNodeIds.contains(n.id));
+        }
+    }
+}
Index: trunk/test/functional/test-functional-env.properties
===================================================================
--- trunk/test/functional/test-functional-env.properties	(revision 1805)
+++ 	(revision )
@@ -1,19 +1,0 @@
-#
-# This file includes properties which are used by all JOSM functional tests 
-#
-
-#### 
-# josm.home - the home directory of the JOSM installation to be used in functional tests
-#
-# This is the home directory for JOSM preferences: ${josm.home}\preferences
-# This is the home directory for JOSM plugins: ${josm.home}\plugins\*.jar
-#
-josm.home=C:\\data\\projekte\\osm\\tag-editor-plugin
-
-
-####
-# test.functional.tempdir - the directory in which functional test case can save
-#   temporary results 
-#
-test.functional.tempdir=C:\\data\\projekte\\eclipse-3.4.1-ws\\JOSM-new\\test\\data\\temp
-
Index: trunk/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java	(revision 1806)
+++ trunk/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java	(revision 1806)
@@ -0,0 +1,20 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class RelationTest {
+    @Test(expected=NullPointerException.class)
+    public void createNewRelation() {
+        new Relation(null);
+    }
+
+    @Test
+    public void equalSemenaticsToNull() {
+        Relation relation = new Relation();
+        assertFalse(relation.hasEqualTechnicalAttributes(null));
+    }
+
+}
