Ignore:
Timestamp:
2009-10-24T21:22:49+02:00 (17 years ago)
Author:
jttt
Message:

Use PrimitiveData for Copy, Paste and Paste tags actions

Location:
trunk/src/org/openstreetmap/josm/actions
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/CopyAction.java

    r2283 r2305  
    77import java.awt.event.ActionEvent;
    88import java.awt.event.KeyEvent;
    9 import java.util.ArrayList;
    109import java.util.Collection;
    11 import java.util.HashMap;
    1210import java.util.LinkedList;
    13 import java.util.List;
    1411
    1512import javax.swing.JOptionPane;
    1613
    1714import org.openstreetmap.josm.Main;
    18 import org.openstreetmap.josm.data.osm.DataSet;
    19 import org.openstreetmap.josm.data.osm.Node;
    2015import org.openstreetmap.josm.data.osm.OsmPrimitive;
    21 import org.openstreetmap.josm.data.osm.Relation;
    22 import org.openstreetmap.josm.data.osm.RelationMember;
    23 import org.openstreetmap.josm.data.osm.Way;
    24 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
    2516import org.openstreetmap.josm.tools.Shortcut;
    2617
     
    4334        if(isEmptySelection()) return;
    4435
    45         Main.pasteBuffer = copyData();
     36        Main.pasteBuffer.makeCopy(getCurrentDataSet().getSelected());
    4637        Main.pasteSource = getEditLayer();
    4738        Main.main.menu.paste.setEnabled(true); /* now we have a paste buffer we can make paste available */
     
    5041            a.pasteBufferChanged(Main.pasteBuffer);
    5142        }
    52     }
    53 
    54     public DataSet copyData() {
    55         /* New pasteBuffer - will be assigned to the global one at the end */
    56         final DataSet pasteBuffer = new DataSet();
    57         final HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>();
    58         /* temporarily maps old nodes to new so we can do a true deep copy */
    59 
    60         if(isEmptySelection()) return pasteBuffer;
    61 
    62         /* scan the selected objects, mapping them to copies; when copying a way or relation,
    63          * the copy references the copies of their child objects */
    64         new AbstractVisitor() {
    65             public void visit(Node n) {
    66                 /* check if already in pasteBuffer - e.g. two ways are selected which share a node;
    67                  * or a way and a node in that way is selected, we'll see it twice, once via the
    68                  * way and once directly; and so on. */
    69                 if (map.containsKey(n))
    70                     return;
    71                 Node nnew = new Node(n);
    72                 map.put(n, nnew);
    73                 pasteBuffer.addPrimitive(nnew);
    74             }
    75             public void visit(Way w) {
    76                 /* check if already in pasteBuffer - could have come from a relation, and directly etc. */
    77                 if (map.containsKey(w))
    78                     return;
    79                 Way wnew = new Way();
    80                 wnew.cloneFrom(w);
    81                 map.put(w, wnew);
    82                 List<Node> nodes = new ArrayList<Node>();
    83                 for (Node n : w.getNodes()) {
    84                     if (! map.containsKey(n)) {
    85                         n.visit(this);
    86                     }
    87                     nodes.add((Node)map.get(n));
    88                 }
    89                 wnew.setNodes(nodes);
    90                 pasteBuffer.addPrimitive(wnew);
    91             }
    92             public void visit(Relation e) {
    93                 if (map.containsKey(e))
    94                     return;
    95                 Relation enew = new Relation(e);
    96                 map.put(e, enew);
    97                 List<RelationMember> members = new ArrayList<RelationMember>();
    98                 for (RelationMember m : e.getMembers()) {
    99                     if (! map.containsKey(m.getMember())) {
    100                         m.getMember().visit(this);
    101                     }
    102                     RelationMember mnew = new RelationMember(m.getRole(), map.get(m.getMember()));
    103                     members.add(mnew);
    104                 }
    105                 enew.setMembers(members);
    106                 pasteBuffer.addPrimitive(enew);
    107             }
    108             public void visitAll() {
    109                 for (OsmPrimitive osm : getCurrentDataSet().getSelected()) {
    110                     osm.visit(this);
    111                 }
    112             }
    113         }.visitAll();
    114 
    115         return pasteBuffer;
    11643    }
    11744
  • trunk/src/org/openstreetmap/josm/actions/DuplicateAction.java

    r2256 r2305  
    1010
    1111import org.openstreetmap.josm.data.osm.OsmPrimitive;
     12import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
    1213import org.openstreetmap.josm.tools.Shortcut;
    1314
     
    2122
    2223    public void actionPerformed(ActionEvent e) {
    23         new PasteAction().pasteData(new CopyAction().copyData(), getEditLayer(), e);
     24        new PasteAction().pasteData(new PrimitiveDeepCopy(getCurrentDataSet().getSelected()), getEditLayer(), e);
    2425    }
    2526
  • trunk/src/org/openstreetmap/josm/actions/JosmAction.java

    r2260 r2305  
    1313import org.openstreetmap.josm.data.osm.DataSet;
    1414import org.openstreetmap.josm.data.osm.OsmPrimitive;
     15import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
    1516import org.openstreetmap.josm.gui.layer.Layer;
    1617import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     
    2223/**
    2324 * Base class helper for all Actions in JOSM. Just to make the life easier.
    24  * 
     25 *
    2526 * A JosmAction is a {@see LayerChangeListener} and a {@see SelectionChangedListener}. Upon
    2627 * a layer change event or a selection change event it invokes {@see #updateEnabled()}.
     
    2829 * of a JosmAction depending on the {@see #getCurrentDataSet()} and the current layers
    2930 * (see also {@see #getEditLayer()}).
    30  * 
     31 *
    3132 * destroy() from interface Destroyable is called e.g. for MapModes, when the last layer has
    3233 * been removed and so the mapframe will be destroyed. For other JosmActions, destroy() may never
     
    105106     * needs to be overridden to be useful
    106107     */
    107     public void pasteBufferChanged(DataSet newPasteBuffer) {
     108    public void pasteBufferChanged(PrimitiveDeepCopy newPasteBuffer) {
    108109        return;
    109110    }
     
    126127    /**
    127128     * Replies the current edit layer
    128      * 
     129     *
    129130     * @return the current edit layer. null, if no edit layer exists
    130131     */
     
    135136    /**
    136137     * Replies the current dataset
    137      * 
     138     *
    138139     * @return the current dataset. null, if no current dataset exists
    139140     */
     
    155156     * Override in subclasses to init the enabled state of an action when it is
    156157     * created. Default behaviour is to call {@see #updateEnabledState()}
    157      * 
     158     *
    158159     * @see #updateEnabledState()
    159160     * @see #updateEnabledState(Collection)
     
    166167     * Override in subclasses to update the enabled state of the action when
    167168     * something in the JOSM state changes, i.e. when a layer is removed or added.
    168      * 
     169     *
    169170     * See {@see #updateEnabledState(Collection)} to respond to changes in the collection
    170171     * of selected primitives.
    171      * 
     172     *
    172173     * Default behavior is empty.
    173      * 
     174     *
    174175     * @see #updateEnabledState(Collection)
    175176     * @see #initEnabledState()
     
    183184     * new selection. Avoid calling getCurrentDataSet().getSelected() because this
    184185     * loops over the complete data set.
    185      * 
     186     *
    186187     * @param selection the collection of selected primitives
    187      * 
     188     *
    188189     * @see #updateEnabledState()
    189190     * @see #initEnabledState()
  • trunk/src/org/openstreetmap/josm/actions/MoveAction.java

    r2256 r2305  
    8181                disty = 0;
    8282                distx = -distx;
     83                break;
    8384            default:
    8485                disty = 0;
  • trunk/src/org/openstreetmap/josm/actions/PasteAction.java

    r2070 r2305  
    88import java.awt.event.KeyEvent;
    99import java.util.ArrayList;
    10 import java.util.Collection;
    1110import java.util.HashMap;
    12 import java.util.LinkedList;
    1311import java.util.List;
     12import java.util.ListIterator;
     13import java.util.Map;
    1414
    1515import org.openstreetmap.josm.Main;
    16 import org.openstreetmap.josm.command.AddCommand;
    17 import org.openstreetmap.josm.command.Command;
    18 import org.openstreetmap.josm.command.SequenceCommand;
     16import org.openstreetmap.josm.command.AddPrimitivesCommand;
    1917import org.openstreetmap.josm.data.coor.EastNorth;
    20 import org.openstreetmap.josm.data.osm.DataSet;
    21 import org.openstreetmap.josm.data.osm.Node;
    22 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    23 import org.openstreetmap.josm.data.osm.Relation;
    24 import org.openstreetmap.josm.data.osm.RelationMember;
    25 import org.openstreetmap.josm.data.osm.Way;
     18import org.openstreetmap.josm.data.osm.NodeData;
     19import org.openstreetmap.josm.data.osm.PrimitiveData;
     20import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
     21import org.openstreetmap.josm.data.osm.RelationData;
     22import org.openstreetmap.josm.data.osm.RelationMemberData;
     23import org.openstreetmap.josm.data.osm.WayData;
    2624import org.openstreetmap.josm.gui.layer.Layer;
    2725import org.openstreetmap.josm.tools.Shortcut;
     
    4038    }
    4139
    42     public  void pasteData(DataSet pasteBuffer, Layer source, ActionEvent e) {
     40    public  void pasteData(PrimitiveDeepCopy pasteBuffer, Layer source, ActionEvent e) {
    4341        /* Find the middle of the pasteBuffer area */
    4442        double maxEast = -1E100, minEast = 1E100, maxNorth = -1E100, minNorth = 1E100;
    45         for (Node n : pasteBuffer.nodes) {
    46             double east = n.getEastNorth().east();
    47             double north = n.getEastNorth().north();
    48             if (east > maxEast) { maxEast = east; }
    49             if (east < minEast) { minEast = east; }
    50             if (north > maxNorth) { maxNorth = north; }
    51             if (north < minNorth) { minNorth = north; }
     43        for (PrimitiveData data : pasteBuffer.getAll()) {
     44            if (data instanceof NodeData) {
     45                NodeData n = (NodeData)data;
     46                double east = n.getEastNorth().east();
     47                double north = n.getEastNorth().north();
     48                if (east > maxEast) { maxEast = east; }
     49                if (east < minEast) { minEast = east; }
     50                if (north > maxNorth) { maxNorth = north; }
     51                if (north < minNorth) { minNorth = north; }
     52            }
    5253        }
    5354
     
    6364        double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0;
    6465
    65         HashMap<OsmPrimitive,OsmPrimitive> map = new HashMap<OsmPrimitive,OsmPrimitive>();
    66         /* temporarily maps old nodes to new so we can do a true deep copy */
    6766
    68         /* do the deep copy of the paste buffer contents, leaving the pasteBuffer unchanged */
    69         for (Node n : pasteBuffer.nodes) {
    70             Node nnew = new Node(n);
    71             nnew.clearOsmId();
    72             if (Main.map.mapView.getEditLayer() == source) {
    73                 nnew.setEastNorth(nnew.getEastNorth().add(offsetEast, offsetNorth));
    74             }
    75             map.put(n, nnew);
     67
     68        // Make a copy of pasteBuffer and map from old id to copied data id
     69        List<PrimitiveData> bufferCopy = new ArrayList<PrimitiveData>();
     70        Map<Long, Long> newIds = new HashMap<Long, Long>();
     71        for (PrimitiveData data:pasteBuffer.getAll()) {
     72            PrimitiveData copy = data.makeCopy();
     73            copy.clearOsmId();
     74            newIds.put(data.getId(), copy.getId());
     75            bufferCopy.add(copy);
    7676        }
    77         for (Way w : pasteBuffer.ways) {
    78             Way wnew = new Way();
    79             wnew.cloneFrom(w);
    80             wnew.clearOsmId();
    81             /* make sure we reference the new nodes corresponding to the old ones */
    82             List<Node> nodes = new ArrayList<Node>();
    83             for (Node n : w.getNodes()) {
    84                 nodes.add((Node)map.get(n));
    85             }
    86             wnew.setNodes(nodes);
    87             map.put(w, wnew);
    88         }
    89         for (Relation r : pasteBuffer.relations) {
    90             Relation rnew = new Relation(r);
    91             r.clearOsmId();
    92             List<RelationMember> members = new ArrayList<RelationMember>();
    93             for (RelationMember m : r.getMembers()) {
    94                 OsmPrimitive mo = map.get(m.getMember());
    95                 if(mo != null) /* FIXME - This only prevents illegal data, but kills the relation */
    96                 {
    97                     RelationMember mnew = new RelationMember(m.getRole(), map.get(m.getMember()));
    98                     members.add(mnew);
     77
     78        // Update references in copied buffer
     79        for (PrimitiveData data:bufferCopy) {
     80            if (data instanceof NodeData) {
     81                NodeData nodeData = (NodeData)data;
     82                if (Main.map.mapView.getEditLayer() == source) {
     83                    nodeData.setEastNorth(nodeData.getEastNorth().add(offsetEast, offsetNorth));
     84                }
     85            } else if (data instanceof WayData) {
     86                ListIterator<Long> it = ((WayData)data).getNodes().listIterator();
     87                while (it.hasNext()) {
     88                    it.set(newIds.get(it.next()));
     89                }
     90            } else if (data instanceof RelationData) {
     91                ListIterator<RelationMemberData> it = ((RelationData)data).getMembers().listIterator();
     92                while (it.hasNext()) {
     93                    RelationMemberData member = it.next();
     94                    it.set(new RelationMemberData(member.getRole(), member.getMemberType(), newIds.get(member.getMemberId())));
    9995                }
    10096            }
    101             rnew.setMembers(members);
    102             map.put(r, rnew);
    10397        }
    10498
    105         /* Now execute the commands to add the dupicated contents of the paste buffer to the map */
    106         Collection<OsmPrimitive> osms = map.values();
    107         Collection<Command> clist = new LinkedList<Command>();
    108         for (OsmPrimitive osm : osms) {
    109             clist.add(new AddCommand(osm));
    110         }
     99        /* Now execute the commands to add the duplicated contents of the paste buffer to the map */
    111100
    112         Main.main.undoRedo.add(new SequenceCommand(tr("Paste"), clist));
    113         getCurrentDataSet().setSelected(osms);
     101        Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy));
     102        //getCurrentDataSet().setSelected(osms);
    114103        Main.map.mapView.repaint();
    115104    }
     
    121110            return;
    122111        }
    123         setEnabled(
    124                 !Main.pasteBuffer.nodes.isEmpty()
    125                 || !Main.pasteBuffer.ways.isEmpty()
    126                 || !Main.pasteBuffer.relations.isEmpty()
    127         );
     112        setEnabled(!Main.pasteBuffer.isEmpty());
    128113    }
    129114}
  • trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java

    r2256 r2305  
    1818import org.openstreetmap.josm.command.Command;
    1919import org.openstreetmap.josm.command.SequenceCommand;
    20 import org.openstreetmap.josm.data.osm.DataSet;
    21 import org.openstreetmap.josm.data.osm.Node;
    2220import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2321import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    24 import org.openstreetmap.josm.data.osm.Relation;
     22import org.openstreetmap.josm.data.osm.PrimitiveData;
     23import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
    2524import org.openstreetmap.josm.data.osm.TagCollection;
    26 import org.openstreetmap.josm.data.osm.Way;
    2725import org.openstreetmap.josm.gui.conflict.tags.PasteTagsConflictResolverDialog;
    2826import org.openstreetmap.josm.tools.Shortcut;
     
    3735    }
    3836
    39     static private List<Class<? extends OsmPrimitive>> osmPrimitiveClasses;
    40     static {
    41         osmPrimitiveClasses = new ArrayList<Class<? extends OsmPrimitive>>();
    42         osmPrimitiveClasses.add(Node.class);
    43         osmPrimitiveClasses.add(Way.class);
    44         osmPrimitiveClasses.add(Relation.class);
    45     }
    46 
    47     /**
    48      * Replies true if the source for tag pasting is heterogeneous, i.e. if it doesn't consist of
    49      * {@see OsmPrimitive}s of exactly one type
    50      *
    51      * @return
    52      */
    53     protected boolean isHeteogeneousSource() {
    54         int count = 0;
    55         count = !getSourcePrimitivesByType(Node.class).isEmpty() ? count + 1 : count;
    56         count = !getSourcePrimitivesByType(Way.class).isEmpty() ? count + 1 : count;
    57         count = !getSourcePrimitivesByType(Relation.class).isEmpty() ? count + 1 : count;
    58         return count > 1;
    59     }
    60 
    61     /**
    62      * Replies all primitives of type <code>type</code> in the current selection.
    63      *
    64      * @param <T>
    65      * @param type  the type
    66      * @return all primitives of type <code>type</code> in the current selection.
    67      */
    68     protected <T extends OsmPrimitive> Collection<? extends OsmPrimitive> getSourcePrimitivesByType(Class<T> type) {
    69         return OsmPrimitive.getFilteredList(Main.pasteBuffer.getSelected(), type);
    70     }
    71 
    72     /**
    73      * Replies the collection of tags for all primitives of type <code>type</code> in the current
    74      * selection
    75      *
    76      * @param <T>
    77      * @param type  the type
    78      * @return the collection of tags for all primitives of type <code>type</code> in the current
    79      * selection
    80      */
    81     protected <T extends OsmPrimitive> TagCollection getSourceTagsByType(Class<T> type) {
    82         return TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
    83     }
    84 
    85     /**
    86      * Replies true if there is at least one tag in the current selection for primitives of
    87      * type <code>type</code>
    88      *
    89      * @param <T>
    90      * @param type the type
    91      * @return true if there is at least one tag in the current selection for primitives of
    92      * type <code>type</code>
    93      */
    94     protected <T extends OsmPrimitive> boolean hasSourceTagsByType(Class<T> type) {
    95         return ! getSourceTagsByType(type).isEmpty();
    96     }
    97 
    98     protected Command buildChangeCommand(Collection<? extends OsmPrimitive> selection, TagCollection tc) {
    99         List<Command> commands = new ArrayList<Command>();
    100         for (String key : tc.getKeys()) {
    101             String value = tc.getValues(key).iterator().next();
    102             value = value.equals("") ? null : value;
    103             commands.add(new ChangePropertyCommand(selection,key,value));
    104         }
    105         if (!commands.isEmpty()) {
    106             String title1 = trn("Pasting {0} tag", "Pasting {0} tags", tc.getKeys().size(), tc.getKeys().size());
    107             String title2 = trn("to {0} primitive", "to {0} primtives", selection.size(), selection.size());
    108             return new SequenceCommand(
    109                     title1 + " " + title2,
    110                     commands
    111             );
    112         }
    113         return null;
    114     }
    115 
    116     protected Map<OsmPrimitiveType, Integer> getSourceStatistics() {
    117         HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
    118         for (Class<? extends OsmPrimitive> type: osmPrimitiveClasses) {
    119             if (!getSourceTagsByType(type).isEmpty()) {
    120                 ret.put(OsmPrimitiveType.from(type), getSourcePrimitivesByType(type).size());
    121             }
    122         }
    123         return ret;
    124     }
    125 
    126     protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
    127         HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
    128         for (Class<? extends OsmPrimitive> type: osmPrimitiveClasses) {
    129             int count = OsmPrimitive.getFilteredList(getEditLayer().data.getSelected(), type).size();
    130             if (count > 0) {
    131                 ret.put(OsmPrimitiveType.from(type), count);
    132             }
    133         }
    134         return ret;
    135     }
    136 
    137     /**
    138      * Pastes the tags from a homogeneous source (i.e. the {@see Main#pasteBuffer}s selection consisting
    139      * of one type of {@see OsmPrimitive}s only.
    140      *
    141      * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
    142      * regardless of their type, receive the same tags.
    143      *
    144      * @param targets the collection of target primitives
    145      */
    146     protected void pasteFromHomogeneousSource(Collection<? extends OsmPrimitive> targets) {
    147         TagCollection tc = null;
    148         for (Class<? extends OsmPrimitive> type : osmPrimitiveClasses) {
    149             TagCollection tc1 = getSourceTagsByType(type);
    150             if (!tc1.isEmpty()) {
    151                 tc = tc1;
    152             }
    153         }
    154         if (tc == null)
    155             // no tags found to paste. Abort.
    156             return;
    157 
    158         if (!tc.isApplicableToPrimitive()) {
    159             PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
    160             dialog.populate(tc, getSourceStatistics(), getTargetStatistics());
    161             dialog.setVisible(true);
    162             if (dialog.isCanceled())
     37    public static class TagPaster {
     38
     39        private final Collection<PrimitiveData> source;
     40        private final Collection<OsmPrimitive> target;
     41        private final List<Command> commands = new ArrayList<Command>();
     42
     43        public TagPaster(Collection<PrimitiveData> source, Collection<OsmPrimitive> target) {
     44            this.source = source;
     45            this.target = target;
     46        }
     47
     48        /**
     49         * Replies true if the source for tag pasting is heterogeneous, i.e. if it doesn't consist of
     50         * {@see OsmPrimitive}s of exactly one type
     51         *
     52         * @return
     53         */
     54        protected boolean isHeteogeneousSource() {
     55            int count = 0;
     56            count = !getSourcePrimitivesByType(OsmPrimitiveType.NODE).isEmpty() ? count + 1 : count;
     57            count = !getSourcePrimitivesByType(OsmPrimitiveType.WAY).isEmpty() ? count + 1 : count;
     58            count = !getSourcePrimitivesByType(OsmPrimitiveType.RELATION).isEmpty() ? count + 1 : count;
     59            return count > 1;
     60        }
     61
     62        /**
     63         * Replies all primitives of type <code>type</code> in the current selection.
     64         *
     65         * @param <T>
     66         * @param type  the type
     67         * @return all primitives of type <code>type</code> in the current selection.
     68         */
     69        protected <T extends PrimitiveData> Collection<? extends PrimitiveData> getSourcePrimitivesByType(OsmPrimitiveType type) {
     70            return PrimitiveData.getFilteredList(source, type);
     71        }
     72
     73        /**
     74         * Replies the collection of tags for all primitives of type <code>type</code> in the current
     75         * selection
     76         *
     77         * @param <T>
     78         * @param type  the type
     79         * @return the collection of tags for all primitives of type <code>type</code> in the current
     80         * selection
     81         */
     82        protected <T extends OsmPrimitive> TagCollection getSourceTagsByType(OsmPrimitiveType type) {
     83            return TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
     84        }
     85
     86        /**
     87         * Replies true if there is at least one tag in the current selection for primitives of
     88         * type <code>type</code>
     89         *
     90         * @param <T>
     91         * @param type the type
     92         * @return true if there is at least one tag in the current selection for primitives of
     93         * type <code>type</code>
     94         */
     95        protected <T extends OsmPrimitive> boolean hasSourceTagsByType(OsmPrimitiveType type) {
     96            return ! getSourceTagsByType(type).isEmpty();
     97        }
     98
     99        protected Command buildChangeCommand(Collection<? extends OsmPrimitive> selection, TagCollection tc) {
     100            List<Command> commands = new ArrayList<Command>();
     101            for (String key : tc.getKeys()) {
     102                String value = tc.getValues(key).iterator().next();
     103                value = value.equals("") ? null : value;
     104                commands.add(new ChangePropertyCommand(selection,key,value));
     105            }
     106            if (!commands.isEmpty()) {
     107                String title1 = trn("Pasting {0} tag", "Pasting {0} tags", tc.getKeys().size(), tc.getKeys().size());
     108                String title2 = trn("to {0} primitive", "to {0} primtives", selection.size(), selection.size());
     109                return new SequenceCommand(
     110                        title1 + " " + title2,
     111                        commands
     112                );
     113            }
     114            return null;
     115        }
     116
     117        protected Map<OsmPrimitiveType, Integer> getSourceStatistics() {
     118            HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
     119            for (OsmPrimitiveType type: OsmPrimitiveType.values()) {
     120                if (!getSourceTagsByType(type).isEmpty()) {
     121                    ret.put(type, getSourcePrimitivesByType(type).size());
     122                }
     123            }
     124            return ret;
     125        }
     126
     127        protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
     128            HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
     129            for (OsmPrimitiveType type: OsmPrimitiveType.values()) {
     130                int count = OsmPrimitive.getFilteredList(target, type.getOsmClass()).size();
     131                if (count > 0) {
     132                    ret.put(type, count);
     133                }
     134            }
     135            return ret;
     136        }
     137
     138        /**
     139         * Pastes the tags from a homogeneous source (i.e. the {@see Main#pasteBuffer}s selection consisting
     140         * of one type of {@see OsmPrimitive}s only.
     141         *
     142         * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
     143         * regardless of their type, receive the same tags.
     144         *
     145         * @param targets the collection of target primitives
     146         */
     147        protected void pasteFromHomogeneousSource() {
     148            TagCollection tc = null;
     149            for (OsmPrimitiveType type : OsmPrimitiveType.values()) {
     150                TagCollection tc1 = getSourceTagsByType(type);
     151                if (!tc1.isEmpty()) {
     152                    tc = tc1;
     153                }
     154            }
     155            if (tc == null)
     156                // no tags found to paste. Abort.
    163157                return;
    164             Command cmd = buildChangeCommand(targets, dialog.getResolution());
    165             Main.main.undoRedo.add(cmd);
    166         } else {
    167             // no conflicts in the source tags to resolve. Just apply the tags
    168             // to the target primitives
    169             //
    170             Command cmd = buildChangeCommand(targets, tc);
    171             Main.main.undoRedo.add(cmd);
    172         }
    173     }
    174 
    175     /**
    176      * Replies true if there is at least one primitive of type <code>type</code> in the collection
    177      * <code>selection</code>
    178      *
    179      * @param <T>
    180      * @param selection  the collection of primitives
    181      * @param type  the type to look for
    182      * @return true if there is at least one primitive of type <code>type</code> in the collection
    183      * <code>selection</code>
    184      */
    185     protected <T extends OsmPrimitive> boolean hasTargetPrimitives(Collection<OsmPrimitive> selection, Class<T> type) {
    186         return !OsmPrimitive.getFilteredList(selection, type).isEmpty();
    187     }
    188 
    189     /**
    190      * Replies true if this a heterogeneous source can be pasted without conflict to targets
    191      *
    192      * @param targets the collection of target primitives
    193      * @return true if this a heterogeneous source can be pasted without conflicts to targets
    194      */
    195     protected boolean canPasteFromHeterogeneousSourceWithoutConflict(Collection<OsmPrimitive> targets) {
    196         if (hasTargetPrimitives(targets, Node.class)) {
    197             TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(Node.class));
    198             if (!tc.isEmpty() && ! tc.isApplicableToPrimitive())
    199                 return false;
    200         }
    201         if (hasTargetPrimitives(targets, Way.class)) {
    202             TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(Way.class));
    203             if (!tc.isEmpty() && ! tc.isApplicableToPrimitive())
    204                 return false;
    205         }
    206         if (hasTargetPrimitives(targets, Relation.class)) {
    207             TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(Relation.class));
    208             if (!tc.isEmpty() && ! tc.isApplicableToPrimitive())
    209                 return false;
    210         }
    211         return true;
    212     }
    213 
    214     /**
    215      * Pastes the tags in the current selection of the paste buffer to a set of target
    216      * primitives.
    217      *
    218      * @param targets the collection of target primitives
    219      */
    220     protected void pasteFromHeterogeneousSource(Collection<OsmPrimitive> targets) {
    221         if (canPasteFromHeterogeneousSourceWithoutConflict(targets)) {
    222             if (hasSourceTagsByType(Node.class) && hasTargetPrimitives(targets, Node.class)) {
    223                 Command cmd = buildChangeCommand(targets, getSourceTagsByType(Node.class));
    224                 Main.main.undoRedo.add(cmd);
    225             }
    226             if (hasSourceTagsByType(Way.class) && hasTargetPrimitives(targets, Way.class)) {
    227                 Command cmd = buildChangeCommand(targets, getSourceTagsByType(Way.class));
    228                 Main.main.undoRedo.add(cmd);
    229             }
    230             if (hasSourceTagsByType(Relation.class) && hasTargetPrimitives(targets, Relation.class)) {
    231                 Command cmd = buildChangeCommand(targets,getSourceTagsByType(Relation.class));
    232                 Main.main.undoRedo.add(cmd);
    233             }
    234         } else {
    235             PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
    236             dialog.populate(
    237                     getSourceTagsByType(Node.class),
    238                     getSourceTagsByType(Way.class),
    239                     getSourceTagsByType(Relation.class),
    240                     getSourceStatistics(),
    241                     getTargetStatistics()
    242             );
    243             dialog.setVisible(true);
    244             if (dialog.isCanceled())
    245                 return;
    246             if (hasSourceTagsByType(Node.class) && hasTargetPrimitives(targets, Node.class)) {
    247                 Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(targets, Node.class), dialog.getResolution(OsmPrimitiveType.NODE));
    248                 Main.main.undoRedo.add(cmd);
    249             }
    250             if (hasSourceTagsByType(Way.class) && hasTargetPrimitives(targets, Way.class)) {
    251                 Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(targets, Way.class), dialog.getResolution(OsmPrimitiveType.WAY));
    252                 Main.main.undoRedo.add(cmd);
    253             }
    254             if (hasSourceTagsByType(Relation.class) && hasTargetPrimitives(targets, Relation.class)) {
    255                 Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(targets, Relation.class), dialog.getResolution(OsmPrimitiveType.RELATION));
    256                 Main.main.undoRedo.add(cmd);
    257             }
    258         }
    259     }
     158
     159            if (!tc.isApplicableToPrimitive()) {
     160                PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
     161                dialog.populate(tc, getSourceStatistics(), getTargetStatistics());
     162                dialog.setVisible(true);
     163                if (dialog.isCanceled())
     164                    return;
     165                Command cmd = buildChangeCommand(target, dialog.getResolution());
     166                commands.add(cmd);
     167            } else {
     168                // no conflicts in the source tags to resolve. Just apply the tags
     169                // to the target primitives
     170                //
     171                Command cmd = buildChangeCommand(target, tc);
     172                commands.add(cmd);
     173            }
     174        }
     175
     176        /**
     177         * Replies true if there is at least one primitive of type <code>type</code> in the collection
     178         * <code>selection</code>
     179         *
     180         * @param <T>
     181         * @param selection  the collection of primitives
     182         * @param type  the type to look for
     183         * @return true if there is at least one primitive of type <code>type</code> in the collection
     184         * <code>selection</code>
     185         */
     186        protected <T extends OsmPrimitive> boolean hasTargetPrimitives(Class<T> type) {
     187            return !OsmPrimitive.getFilteredList(target, type).isEmpty();
     188        }
     189
     190        /**
     191         * Replies true if this a heterogeneous source can be pasted without conflict to targets
     192         *
     193         * @param targets the collection of target primitives
     194         * @return true if this a heterogeneous source can be pasted without conflicts to targets
     195         */
     196        protected boolean canPasteFromHeterogeneousSourceWithoutConflict(Collection<OsmPrimitive> targets) {
     197            for (OsmPrimitiveType type:OsmPrimitiveType.values()) {
     198                if (hasTargetPrimitives(type.getOsmClass())) {
     199                    TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
     200                    if (!tc.isEmpty() && ! tc.isApplicableToPrimitive())
     201                        return false;
     202                }
     203            }
     204            return true;
     205        }
     206
     207        /**
     208         * Pastes the tags in the current selection of the paste buffer to a set of target
     209         * primitives.
     210         *
     211         * @param targets the collection of target primitives
     212         */
     213        protected void pasteFromHeterogeneousSource() {
     214            if (canPasteFromHeterogeneousSourceWithoutConflict(target)) {
     215                for (OsmPrimitiveType type:OsmPrimitiveType.values()) {
     216                    if (hasSourceTagsByType(type) && hasTargetPrimitives(type.getOsmClass())) {
     217                        Command cmd = buildChangeCommand(target, getSourceTagsByType(type));
     218                        commands.add(cmd);
     219                    }
     220                }
     221            } else {
     222                PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
     223                dialog.populate(
     224                        getSourceTagsByType(OsmPrimitiveType.NODE),
     225                        getSourceTagsByType(OsmPrimitiveType.WAY),
     226                        getSourceTagsByType(OsmPrimitiveType.RELATION),
     227                        getSourceStatistics(),
     228                        getTargetStatistics()
     229                );
     230                dialog.setVisible(true);
     231                if (dialog.isCanceled())
     232                    return;
     233                for (OsmPrimitiveType type:OsmPrimitiveType.values()) {
     234                    if (hasSourceTagsByType(type) && hasTargetPrimitives(type.getOsmClass())) {
     235                        Command cmd = buildChangeCommand(OsmPrimitive.getFilteredList(target, type.getOsmClass()), dialog.getResolution(type));
     236                        commands.add(cmd);
     237                    }
     238                }
     239            }
     240        }
     241
     242        public List<Command> execute() {
     243            commands.clear();
     244            if (isHeteogeneousSource()) {
     245                pasteFromHeterogeneousSource();
     246            } else {
     247                pasteFromHomogeneousSource();
     248            }
     249            return commands;
     250        }
     251
     252    }
     253
    260254
    261255    public void actionPerformed(ActionEvent e) {
    262256        if (getCurrentDataSet().getSelected().isEmpty())
    263257            return;
    264         if (isHeteogeneousSource()) {
    265             pasteFromHeterogeneousSource(getCurrentDataSet().getSelected());
    266         } else {
    267             pasteFromHomogeneousSource(getCurrentDataSet().getSelected());
    268         }
    269     }
    270 
    271     @Override public void pasteBufferChanged(DataSet newPasteBuffer) {
     258        TagPaster tagPaster = new TagPaster(Main.pasteBuffer.getDirectlyAdded(), getCurrentDataSet().getSelected());
     259        for (Command c:tagPaster.execute()) {
     260            Main.main.undoRedo.add(c);
     261        }
     262    }
     263
     264    @Override public void pasteBufferChanged(PrimitiveDeepCopy newPasteBuffer) {
    272265        updateEnabledState();
    273266    }
     
    281274        setEnabled(
    282275                !getCurrentDataSet().getSelected().isEmpty()
    283                 && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getSelected()).isEmpty()
     276                && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getDirectlyAdded()).isEmpty()
    284277        );
    285278    }
     
    289282        setEnabled(
    290283                selection!= null && !selection.isEmpty()
    291                 && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getSelected()).isEmpty()
     284                && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getDirectlyAdded()).isEmpty()
    292285        );
    293286    }
Note: See TracChangeset for help on using the changeset viewer.