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