001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions.upload;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.BorderLayout;
007import java.awt.Dimension;
008import java.util.Iterator;
009import java.util.List;
010
011import javax.swing.JLabel;
012import javax.swing.JPanel;
013import javax.swing.JScrollPane;
014import javax.swing.JTable;
015import javax.swing.table.DefaultTableModel;
016
017import org.openstreetmap.josm.Main;
018import org.openstreetmap.josm.data.APIDataSet;
019import org.openstreetmap.josm.data.osm.Relation;
020import org.openstreetmap.josm.gui.ExtendedDialog;
021import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
022import org.openstreetmap.josm.tools.WindowGeometry;
023
024/**
025 * This upload hook reorders the list of new relations to upload such that child
026 * relations are uploaded before parent relations. It also checks for cyclic
027 * dependencies in the list of new relations.
028 *
029 *
030 */
031public class RelationUploadOrderHook implements UploadHook {
032
033    /**
034     * builds the panel which warns users about a cyclic dependency
035     *
036     * @param dep  the list of relations with a cyclic dependency
037     * @return the panel
038     */
039    protected JPanel buildWarningPanel(List<Relation> dep) {
040        JPanel pnl = new JPanel(new BorderLayout());
041        String msg = tr("<html>{0} relations build a cycle because they refer to each other.<br>"
042                + "JOSM cannot upload them. Please edit the relations and remove the "
043                + "cyclic dependency.</html>", dep.size()-1);
044        pnl.add(new JLabel(msg), BorderLayout.NORTH);
045
046        DefaultTableModel model = new DefaultTableModel();
047        model.addColumn(tr("Relation ..."));
048        model.addColumn(tr("... refers to relation"));
049        for (int i = 0; i < dep.size()-1; i++) {
050            Relation r1 = dep.get(i);
051            Relation r2 = dep.get(i+1);
052            model.addRow(new Relation[] {r1, r2});
053        }
054        JTable tbl = new JTable(model);
055        OsmPrimitivRenderer renderer = new OsmPrimitivRenderer();
056        tbl.getColumnModel().getColumn(0).setCellRenderer(renderer);
057        tbl.getColumnModel().getColumn(1).setCellRenderer(renderer);
058        pnl.add(new JScrollPane(tbl), BorderLayout.CENTER);
059        return pnl;
060    }
061
062    /**
063     * Warns the user if a cyclic dependency is detected
064     *
065     * @param e the cyclic dependency exception
066     */
067    protected void warnCyclicUploadDependency(CyclicUploadDependencyException e) {
068        List<Relation> dep = e.getCyclicUploadDependency();
069        Relation last = dep.get(dep.size() -1);
070        Iterator<Relation> it = dep.iterator();
071        while (it.hasNext()) {
072            if (it.next() != last) {
073                it.remove();
074            } else {
075                break;
076            }
077        }
078        JPanel pnl = buildWarningPanel(dep);
079        ExtendedDialog dialog = new ExtendedDialog(
080                Main.parent,
081                tr("Cycling dependencies"),
082                new String[] {tr("OK")}
083        );
084        dialog.setContent(pnl, false /* don't embed in scroll pane */);
085        dialog.setButtonIcons(new String[] {"ok"});
086        dialog.setRememberWindowGeometry(
087                getClass().getName() + ".geometry",
088                WindowGeometry.centerInWindow(Main.parent, new Dimension(300, 300))
089        );
090        dialog.showDialog();
091    }
092
093    @Override
094    public boolean checkUpload(APIDataSet apiDataSet) {
095        try {
096            apiDataSet.adjustRelationUploadOrder();
097            return true;
098        } catch (CyclicUploadDependencyException e) {
099            warnCyclicUploadDependency(e);
100            return false;
101        }
102    }
103}