001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.relation;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.datatransfer.Transferable;
007import java.awt.datatransfer.UnsupportedFlavorException;
008import java.io.IOException;
009import java.util.ArrayList;
010import java.util.Collection;
011
012import javax.swing.JComponent;
013import javax.swing.JTable;
014import javax.swing.TransferHandler;
015
016import org.openstreetmap.josm.data.osm.OsmPrimitive;
017import org.openstreetmap.josm.data.osm.PrimitiveData;
018import org.openstreetmap.josm.data.osm.PrimitiveId;
019import org.openstreetmap.josm.data.osm.RelationMember;
020import org.openstreetmap.josm.data.osm.RelationMemberData;
021import org.openstreetmap.josm.gui.datatransfer.RelationMemberTransferable;
022import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
023import org.openstreetmap.josm.tools.Logging;
024
025/**
026 * A transfer handler that helps with importing / exporting members for relations.
027 * @author Michael Zangl
028 * @since 10604
029 */
030public class MemberTransferHandler extends TransferHandler {
031
032    @Override
033    public int getSourceActions(JComponent c) {
034        return COPY_OR_MOVE;
035    }
036
037    @Override
038    protected Transferable createTransferable(JComponent c) {
039        final MemberTable source = (MemberTable) c;
040        return new RelationMemberTransferable(source.getMemberTableModel().getSelectedMembers());
041    }
042
043    @Override
044    public boolean canImport(TransferSupport support) {
045        if (support.isDrop()) {
046            support.setShowDropLocation(true);
047        }
048        return support.isDataFlavorSupported(RelationMemberTransferable.RELATION_MEMBER_DATA)
049                || support.isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR);
050    }
051
052    @Override
053    public boolean importData(TransferSupport support) {
054        MemberTable destination = (MemberTable) support.getComponent();
055        int insertRow = computeInsertionRow(support, destination);
056
057        return importDataAt(support, destination, insertRow);
058    }
059
060    private static int computeInsertionRow(TransferSupport support, MemberTable destination) {
061        final int insertRow;
062        if (support.isDrop()) {
063            DropLocation dl = support.getDropLocation();
064            if (dl instanceof JTable.DropLocation) {
065                insertRow = ((JTable.DropLocation) dl).getRow();
066            } else {
067                insertRow = 0;
068            }
069        } else {
070            int selection = destination.getSelectedRow();
071            if (selection < 0) {
072                // no selection, add at the end.
073                insertRow = destination.getRowCount();
074            } else {
075                insertRow = selection;
076            }
077        }
078        return insertRow;
079    }
080
081    private boolean importDataAt(TransferSupport support, MemberTable destination, int insertRow) {
082        try {
083            if (support.isDataFlavorSupported(RelationMemberTransferable.RELATION_MEMBER_DATA)) {
084                importRelationMemberData(support, destination, insertRow);
085                return true;
086            } else if (support.isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR)) {
087                importPrimitiveData(support, destination, insertRow);
088                return true;
089            } else {
090                return false;
091            }
092        } catch (IOException | UnsupportedFlavorException e) {
093            Logging.warn(e);
094            return false;
095        }
096    }
097
098    protected void importRelationMemberData(TransferSupport support, final MemberTable destination, int insertRow)
099            throws UnsupportedFlavorException, IOException {
100        final RelationMemberTransferable.Data memberData = (RelationMemberTransferable.Data)
101                support.getTransferable().getTransferData(RelationMemberTransferable.RELATION_MEMBER_DATA);
102        importData(destination, insertRow, memberData.getRelationMemberData(), new AbstractRelationMemberConverter<RelationMemberData>() {
103            @Override
104            protected RelationMember getMember(MemberTable destination, RelationMemberData data, OsmPrimitive p) {
105                return new RelationMember(data.getRole(), p);
106            }
107        });
108    }
109
110    protected void importPrimitiveData(TransferSupport support, final MemberTable destination, int insertRow)
111            throws UnsupportedFlavorException, IOException {
112        final PrimitiveTransferData data = (PrimitiveTransferData)
113                support.getTransferable().getTransferData(PrimitiveTransferData.DATA_FLAVOR);
114        importData(destination, insertRow, data.getDirectlyAdded(), new AbstractRelationMemberConverter<PrimitiveData>() {
115            @Override
116            protected RelationMember getMember(MemberTable destination, PrimitiveData data, OsmPrimitive p) {
117                return destination.getMemberTableModel().getRelationMemberForPrimitive(p);
118            }
119        });
120    }
121
122    protected <T extends PrimitiveId> void importData(MemberTable destination, int insertRow,
123                                  Collection<T> memberData, AbstractRelationMemberConverter<T> toMemberFunction) {
124        final Collection<RelationMember> membersToAdd = new ArrayList<>(memberData.size());
125        for (T data : memberData) {
126            final RelationMember member = toMemberFunction.importPrimitive(destination, data);
127            if (member != null) {
128                membersToAdd.add(member);
129            }
130        }
131        destination.getMemberTableModel().addMembersAtIndexKeepingOldSelection(membersToAdd, insertRow);
132    }
133
134    @Override
135    protected void exportDone(JComponent sourceComponent, Transferable data, int action) {
136        if (action != MOVE) {
137            return;
138        }
139        final MemberTable source = (MemberTable) sourceComponent;
140        final MemberTableModel model = source.getMemberTableModel();
141        model.remove(source.getSelectedRows());
142        model.selectionChanged(null);
143    }
144
145    private abstract static class AbstractRelationMemberConverter<T extends PrimitiveId> {
146        protected RelationMember importPrimitive(MemberTable destination, T data) {
147            final OsmPrimitive p = destination.getLayer().data.getPrimitiveById(data);
148            if (p == null) {
149                Logging.warn(tr("Cannot add {0} since it is not part of dataset", data));
150                return null;
151            } else {
152                return getMember(destination, data, p);
153            }
154        }
155
156        protected abstract RelationMember getMember(MemberTable destination, T data, OsmPrimitive p);
157    }
158}