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