Changeset 10737 in josm


Ignore:
Timestamp:
2016-08-05T20:09:55+02:00 (3 years ago)
Author:
Don-vip
Message:

fix #12900 - Conflicts in pasted tags cannot be resolved, cannot be resolved to "none" (patch by michael2402) - gsoc-core

Location:
trunk
Files:
4 added
1 deleted
9 edited

Legend:

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

    r10692 r10737  
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    55import static org.openstreetmap.josm.tools.I18n.tr;
    6 import static org.openstreetmap.josm.tools.I18n.trn;
    76
    87import java.awt.event.ActionEvent;
    98import java.awt.event.KeyEvent;
    10 import java.util.ArrayList;
    119import java.util.Collection;
    12 import java.util.EnumMap;
    13 import java.util.List;
    14 import java.util.Map;
    15 import java.util.Map.Entry;
    1610
    17 import org.openstreetmap.josm.Main;
    18 import org.openstreetmap.josm.command.ChangePropertyCommand;
    19 import org.openstreetmap.josm.command.Command;
    20 import org.openstreetmap.josm.command.SequenceCommand;
    2111import org.openstreetmap.josm.data.osm.DataSet;
    2212import org.openstreetmap.josm.data.osm.OsmPrimitive;
    23 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    24 import org.openstreetmap.josm.data.osm.PrimitiveData;
    25 import org.openstreetmap.josm.data.osm.Tag;
    26 import org.openstreetmap.josm.data.osm.TagCollection;
    27 import org.openstreetmap.josm.gui.conflict.tags.PasteTagsConflictResolverDialog;
    2813import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
    29 import org.openstreetmap.josm.tools.I18n;
    3014import org.openstreetmap.josm.tools.Shortcut;
    31 import org.openstreetmap.josm.tools.TextTagParser;
    3215
    3316/**
     
    5538    }
    5639
    57     /**
    58      * Used to update the tags.
    59      */
    60     public static class TagPaster {
    61 
    62         private final Collection<PrimitiveData> source;
    63         private final Collection<OsmPrimitive> target;
    64         private final List<Tag> tags = new ArrayList<>();
    65 
    66         /**
    67          * Constructs a new {@code TagPaster}.
    68          * @param source source primitives
    69          * @param target target primitives
    70          */
    71         public TagPaster(Collection<PrimitiveData> source, Collection<OsmPrimitive> target) {
    72             this.source = source;
    73             this.target = target;
    74         }
    75 
    76         /**
    77          * Determines if the source for tag pasting is heterogeneous, i.e. if it doesn't consist of
    78          * {@link OsmPrimitive}s of exactly one type
    79          * @return true if the source for tag pasting is heterogeneous
    80          */
    81         protected boolean isHeterogeneousSource() {
    82             int count = 0;
    83             count = !getSourcePrimitivesByType(OsmPrimitiveType.NODE).isEmpty() ? (count + 1) : count;
    84             count = !getSourcePrimitivesByType(OsmPrimitiveType.WAY).isEmpty() ? (count + 1) : count;
    85             count = !getSourcePrimitivesByType(OsmPrimitiveType.RELATION).isEmpty() ? (count + 1) : count;
    86             return count > 1;
    87         }
    88 
    89         /**
    90          * Replies all primitives of type <code>type</code> in the current selection.
    91          *
    92          * @param type  the type
    93          * @return all primitives of type <code>type</code> in the current selection.
    94          */
    95         protected Collection<? extends PrimitiveData> getSourcePrimitivesByType(OsmPrimitiveType type) {
    96             return PrimitiveData.getFilteredList(source, type);
    97         }
    98 
    99         /**
    100          * Replies the collection of tags for all primitives of type <code>type</code> in the current
    101          * selection
    102          *
    103          * @param type  the type
    104          * @return the collection of tags for all primitives of type <code>type</code> in the current
    105          * selection
    106          */
    107         protected TagCollection getSourceTagsByType(OsmPrimitiveType type) {
    108             return TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
    109         }
    110 
    111         /**
    112          * Replies true if there is at least one tag in the current selection for primitives of
    113          * type <code>type</code>
    114          *
    115          * @param type the type
    116          * @return true if there is at least one tag in the current selection for primitives of
    117          * type <code>type</code>
    118          */
    119         protected boolean hasSourceTagsByType(OsmPrimitiveType type) {
    120             return !getSourceTagsByType(type).isEmpty();
    121         }
    122 
    123         protected void buildTags(TagCollection tc) {
    124             for (String key : tc.getKeys()) {
    125                 tags.add(new Tag(key, tc.getValues(key).iterator().next()));
    126             }
    127         }
    128 
    129         protected Map<OsmPrimitiveType, Integer> getSourceStatistics() {
    130             Map<OsmPrimitiveType, Integer> ret = new EnumMap<>(OsmPrimitiveType.class);
    131             for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
    132                 if (!getSourceTagsByType(type).isEmpty()) {
    133                     ret.put(type, getSourcePrimitivesByType(type).size());
    134                 }
    135             }
    136             return ret;
    137         }
    138 
    139         protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
    140             Map<OsmPrimitiveType, Integer> ret = new EnumMap<>(OsmPrimitiveType.class);
    141             for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
    142                 int count = OsmPrimitive.getFilteredList(target, type.getOsmClass()).size();
    143                 if (count > 0) {
    144                     ret.put(type, count);
    145                 }
    146             }
    147             return ret;
    148         }
    149 
    150         /**
    151          * Pastes the tags from a homogeneous source (the selection consisting
    152          * of one type of {@link OsmPrimitive}s only).
    153          *
    154          * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
    155          * regardless of their type, receive the same tags.
    156          */
    157         protected void pasteFromHomogeneousSource() {
    158             TagCollection tc = null;
    159             for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
    160                 TagCollection tc1 = getSourceTagsByType(type);
    161                 if (!tc1.isEmpty()) {
    162                     tc = tc1;
    163                 }
    164             }
    165             if (tc == null)
    166                 // no tags found to paste. Abort.
    167                 return;
    168 
    169             if (!tc.isApplicableToPrimitive()) {
    170                 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
    171                 dialog.populate(tc, getSourceStatistics(), getTargetStatistics());
    172                 dialog.setVisible(true);
    173                 if (dialog.isCanceled())
    174                     return;
    175                 buildTags(dialog.getResolution());
    176             } else {
    177                 // no conflicts in the source tags to resolve. Just apply the tags to the target primitives
    178                 buildTags(tc);
    179             }
    180         }
    181 
    182         /**
    183          * Replies true if there is at least one primitive of type <code>type</code>
    184          * is in the target collection
    185          *
    186          * @param type  the type to look for
    187          * @return true if there is at least one primitive of type <code>type</code> in the collection
    188          * <code>selection</code>
    189          */
    190         protected boolean hasTargetPrimitives(Class<? extends OsmPrimitive> type) {
    191             return !OsmPrimitive.getFilteredList(target, type).isEmpty();
    192         }
    193 
    194         /**
    195          * Replies true if this a heterogeneous source can be pasted without conflict to targets
    196          *
    197          * @return true if this a heterogeneous source can be pasted without conflicts to targets
    198          */
    199         protected boolean canPasteFromHeterogeneousSourceWithoutConflict() {
    200             for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
    201                 if (hasTargetPrimitives(type.getOsmClass())) {
    202                     TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
    203                     if (!tc.isEmpty() && !tc.isApplicableToPrimitive())
    204                         return false;
    205                 }
    206             }
    207             return true;
    208         }
    209 
    210         /**
    211          * Pastes the tags in the current selection of the paste buffer to a set of target primitives.
    212          */
    213         protected void pasteFromHeterogeneousSource() {
    214             if (canPasteFromHeterogeneousSourceWithoutConflict()) {
    215                 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
    216                     if (hasSourceTagsByType(type) && hasTargetPrimitives(type.getOsmClass())) {
    217                         buildTags(getSourceTagsByType(type));
    218                     }
    219                 }
    220             } else {
    221                 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
    222                 dialog.populate(
    223                         getSourceTagsByType(OsmPrimitiveType.NODE),
    224                         getSourceTagsByType(OsmPrimitiveType.WAY),
    225                         getSourceTagsByType(OsmPrimitiveType.RELATION),
    226                         getSourceStatistics(),
    227                         getTargetStatistics()
    228                 );
    229                 dialog.setVisible(true);
    230                 if (dialog.isCanceled())
    231                     return;
    232                 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
    233                     if (hasSourceTagsByType(type) && hasTargetPrimitives(type.getOsmClass())) {
    234                         buildTags(dialog.getResolution(type));
    235                     }
    236                 }
    237             }
    238         }
    239 
    240         /**
    241          * Performs the paste operation.
    242          * @return list of tags
    243          */
    244         public List<Tag> execute() {
    245             tags.clear();
    246             if (isHeterogeneousSource()) {
    247                 pasteFromHeterogeneousSource();
    248             } else {
    249                 pasteFromHomogeneousSource();
    250             }
    251             return tags;
    252         }
    253 
    254     }
    255 
    25640    @Override
    25741    public void actionPerformed(ActionEvent e) {
     
    26246
    26347        transferHandler.pasteTags(selection);
    264     }
    265 
    266     /**
    267      * Paste tags from arbitrary text, not using JOSM buffer
    268      * @param selection selected primitives
    269      * @param text text containing tags
    270      * @return true if action was successful
    271      * @see TextTagParser#readTagsFromText
    272      */
    273     public static boolean pasteTagsFromText(Collection<OsmPrimitive> selection, String text) {
    274         Map<String, String> tags = TextTagParser.readTagsFromText(text);
    275         if (tags == null || tags.isEmpty()) {
    276             TextTagParser.showBadBufferMessage(help);
    277             return false;
    278         }
    279         if (!TextTagParser.validateTags(tags)) return false;
    280 
    281         List<Command> commands = new ArrayList<>(tags.size());
    282         for (Entry<String, String> entry: tags.entrySet()) {
    283             String v = entry.getValue();
    284             commands.add(new ChangePropertyCommand(selection, entry.getKey(), "".equals(v) ? null : v));
    285         }
    286         commitCommands(selection, commands);
    287         return !commands.isEmpty();
    288     }
    289 
    290     /**
    291      * Create and execute SequenceCommand with descriptive title
    292      * @param selection selected primitives
    293      * @param commands the commands to perform in a sequential command
    294      */
    295     private static void commitCommands(Collection<OsmPrimitive> selection, List<Command> commands) {
    296         if (!commands.isEmpty()) {
    297             String title1 = trn("Pasting {0} tag", "Pasting {0} tags", commands.size(), commands.size());
    298             String title2 = trn("to {0} object", "to {0} objects", selection.size(), selection.size());
    299             @I18n.QuirkyPluralString
    300             final String title = title1 + ' ' + title2;
    301             Main.main.undoRedo.add(
    302                     new SequenceCommand(
    303                             title,
    304                             commands
    305                     ));
    306         }
    30748    }
    30849
  • trunk/src/org/openstreetmap/josm/data/osm/Tag.java

    r10716 r10737  
    22package org.openstreetmap.josm.data.osm;
    33
     4import java.io.Serializable;
    45import java.util.Collection;
    56import java.util.Collections;
     
    1718 * the modifying methods throw an {@link UnsupportedOperationException}.
    1819 */
    19 public class Tag implements Tagged, Entry<String, String> {
     20public class Tag implements Tagged, Entry<String, String>, Serializable {
     21
     22    private static final long serialVersionUID = 1;
    2023
    2124    private final String key;
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java

    r10593 r10737  
    290290        }
    291291
    292         protected void updateEnabledState() {
     292        void updateEnabledState() {
    293293            if (mode == null) {
    294294                setEnabled(false);
     
    296296                setEnabled(allPrimitivesResolver.getModel().isResolvedCompletely());
    297297            } else {
    298                 boolean enabled = true;
    299                 for (TagConflictResolver val: resolvers.values()) {
    300                     enabled &= val.getModel().isResolvedCompletely();
    301                 }
    302                 setEnabled(enabled);
     298                setEnabled(resolvers.values().stream().allMatch(val -> val.getModel().isResolvedCompletely()));
    303299            }
    304300        }
     
    346342                if (model == resolver.getModel()) {
    347343                    tpResolvers.setIconAt(i,
    348                             (Boolean) evt.getNewValue() ? iconResolved : iconUnresolved
    349 
     344                            (Integer) evt.getNewValue() == 0 ? iconResolved : iconUnresolved
    350345                    );
    351346                }
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolverModel.java

    r10619 r10737  
    2222    private transient TagCollection tags;
    2323    private List<String> displayedKeys;
    24     private Set<String> keysWithConflicts;
     24    private Set<String> keysWithConflicts = new HashSet<>();
    2525    private transient Map<String, MultiValueResolutionDecision> decisions;
    2626    private int numConflicts;
     
    5454
    5555    protected void refreshNumConflicts() {
    56         int count = 0;
    57         for (MultiValueResolutionDecision d : decisions.values()) {
    58             if (!d.isDecided()) {
    59                 count++;
    60             }
    61         }
    62         setNumConflicts(count);
     56        setNumConflicts((int) decisions.values().stream().filter(d -> !d.isDecided()).count());
    6357    }
    6458
     
    122116        this.tags = tags;
    123117        displayedKeys = new ArrayList<>();
    124         this.keysWithConflicts = keysWithConflicts == null ? new HashSet<>() : keysWithConflicts;
     118        if (keysWithConflicts != null) {
     119            this.keysWithConflicts.addAll(keysWithConflicts);
     120        }
    125121        decisions = new HashMap<>();
    126122        rebuild();
     
    183179     */
    184180    public boolean isResolvedCompletely() {
    185         return numConflicts == 0 && keysWithConflicts != null && keysWithConflicts.isEmpty();
    186     }
    187 
     181        return numConflicts == 0;
     182    }
     183
     184    /**
     185     * Gets the number of reamining conflicts.
     186     * @return The number
     187     */
    188188    public int getNumConflicts() {
    189189        return numConflicts;
    190190    }
    191191
     192    /**
     193     * Gets the number of decisions the user can take
     194     * @return The number of decisions
     195     */
    192196    public int getNumDecisions() {
    193197        return decisions == null ? 0 : decisions.size();
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java

    r10639 r10737  
    1717import org.openstreetmap.josm.gui.datatransfer.importers.FilePaster;
    1818import org.openstreetmap.josm.gui.datatransfer.importers.PrimitiveDataPaster;
     19import org.openstreetmap.josm.gui.datatransfer.importers.PrimitiveTagTransferPaster;
    1920import org.openstreetmap.josm.gui.datatransfer.importers.TagTransferPaster;
    2021import org.openstreetmap.josm.gui.datatransfer.importers.TextTagPaster;
     
    3031    private static final Collection<AbstractOsmDataPaster> SUPPORTED = Arrays.asList(
    3132            new FilePaster(), new PrimitiveDataPaster(),
     33            new PrimitiveTagTransferPaster(),
    3234            new TagTransferPaster(), new TextTagPaster());
    3335
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferable.java

    r10605 r10737  
    1212import org.openstreetmap.josm.data.osm.PrimitiveData;
    1313import org.openstreetmap.josm.gui.datatransfer.data.OsmLayerTransferData;
     14import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTagTransferData;
    1415import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    1516import org.openstreetmap.josm.gui.datatransfer.data.TagTransferData;
     
    7576        } else if (PrimitiveTransferData.DATA_FLAVOR.equals(flavor)) {
    7677            return primitives;
     78        } else if (PrimitiveTagTransferData.FLAVOR.equals(flavor)) {
     79            return new PrimitiveTagTransferData(primitives);
    7780        } else if (TagTransferData.FLAVOR.equals(flavor)) {
    7881            return new TagTransferData(primitives.getDirectlyAdded());
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/data/TagTransferData.java

    r10637 r10737  
    1414 * This is a special transfer type that only transfers tag data.
    1515 * <p>
    16  * It currently contains all tags contained in the selection that was copied.
     16 * It contains all tags contained in the selection that was copied. For conflicting tags, any of the values may be used.
    1717 * @author Michael Zangl
    1818 * @since 10604
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/AbstractTagPaster.java

    r10604 r10737  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.datatransfer.importers;
     3
     4import static org.openstreetmap.josm.tools.I18n.trn;
    35
    46import java.awt.datatransfer.DataFlavor;
     
    68import java.io.IOException;
    79import java.util.Collection;
     10import java.util.Collections;
     11import java.util.List;
    812import java.util.Map;
    913
     
    1216import org.openstreetmap.josm.Main;
    1317import org.openstreetmap.josm.command.ChangePropertyCommand;
     18import org.openstreetmap.josm.command.Command;
     19import org.openstreetmap.josm.command.SequenceCommand;
    1420import org.openstreetmap.josm.data.coor.EastNorth;
    1521import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1622import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     23import org.openstreetmap.josm.tools.I18n;
    1724
    1825/**
     
    4249            throws UnsupportedFlavorException, IOException {
    4350        ChangePropertyCommand command = new ChangePropertyCommand(selection, getTags(support));
    44         Main.main.undoRedo.add(command);
     51        commitCommands(selection, Collections.singletonList(command));
    4552        return true;
     53    }
     54
     55    /**
     56     * Create and execute SequenceCommand with descriptive title
     57     * @param selection selected primitives
     58     * @param commands the commands to perform in a sequential command
     59     * @since 10737
     60     */
     61    protected static void commitCommands(Collection<? extends OsmPrimitive> selection, List<Command> commands) {
     62        if (!commands.isEmpty()) {
     63            String title1 = trn("Pasting {0} tag", "Pasting {0} tags", commands.size(), commands.size());
     64            String title2 = trn("to {0} object", "to {0} objects", selection.size(), selection.size());
     65            @I18n.QuirkyPluralString
     66            final String title = title1 + ' ' + title2;
     67            Main.main.undoRedo.add(new SequenceCommand(title, commands));
     68        }
    4669    }
    4770
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/TextTagPaster.java

    r10604 r10737  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.datatransfer.importers;
     3
     4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    35
    46import java.awt.datatransfer.DataFlavor;
     
    1820 */
    1921public final class TextTagPaster extends AbstractTagPaster {
     22    private static final String help = ht("/Action/PasteTags");
    2023
    2124    /**
     
    2932    public boolean supports(TransferSupport support) {
    3033        try {
    31             return super.supports(support) && getTags(support) != null;
     34            return super.supports(support) && containsValidTags(support);
    3235        } catch (UnsupportedFlavorException | IOException e) {
    3336            Main.warn(e);
     
    3639    }
    3740
     41    private boolean containsValidTags(TransferSupport support) throws UnsupportedFlavorException, IOException {
     42        Map<String, String> tags = getTagsImpl(support);
     43        return tags != null && !tags.isEmpty();
     44    }
     45
    3846    @Override
    3947    protected Map<String, String> getTags(TransferSupport support) throws UnsupportedFlavorException, IOException {
     48        Map<String, String> tags = getTagsImpl(support);
     49        if (tags == null || tags.isEmpty()) {
     50            TextTagParser.showBadBufferMessage(help);
     51            throw new IOException("Invalid tags to paste.");
     52        }
     53        if (!TextTagParser.validateTags(tags)) {
     54            throw new IOException("Tags to paste are not valid.");
     55        }
     56        return tags;
     57    }
     58
     59    private Map<String, String> getTagsImpl(TransferSupport support) throws UnsupportedFlavorException, IOException {
    4060        return TextTagParser.readTagsFromText((String) support.getTransferable().getTransferData(df));
    4161    }
Note: See TracChangeset for help on using the changeset viewer.