Ticket #17459: add-reverter-core-hacks-v3.patch

File add-reverter-core-hacks-v3.patch, 21.7 KB (added by GerdP, 6 years ago)
  • src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.osm;
     3import static org.openstreetmap.josm.tools.I18n.tr;
    34
     5import java.util.Collections;
    46import java.util.HashMap;
    57import java.util.Iterator;
    68import java.util.Map;
    79import java.util.Map.Entry;
    810import java.util.Set;
    9 import java.util.stream.Collectors;
    1011
    1112import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
    1213import org.openstreetmap.josm.tools.CheckParameterUtil;
     14import org.openstreetmap.josm.tools.Logging;
    1315
    1416/**
    15  * A ChangesetDataSet holds the content of a changeset.
     17 * A ChangesetDataSet holds the content of a changeset. Typically, a primitive is modified only once in a changeset,
     18 * but if there are multiple modifications, the first and last are kept. Further intermediate versions are not kept.
    1619 */
    1720public class ChangesetDataSet {
    1821
     
    4649        HistoryOsmPrimitive getPrimitive();
    4750    }
    4851
    49     private final Map<PrimitiveId, HistoryOsmPrimitive> primitives = new HashMap<>();
    50     private final Map<PrimitiveId, ChangesetModificationType> modificationTypes = new HashMap<>();
     52    /** maps an id to either one {@link ChangesetDataSetEntry} or an array of {@link ChangesetDataSetEntry} */
     53    private final Map<PrimitiveId, Object> entryMap = new HashMap<>();
    5154
    5255    /**
    5356     * Remembers a history primitive with the given modification type
     
    5659     * @param cmt the modification type. Must not be null.
    5760     * @throws IllegalArgumentException if primitive is null
    5861     * @throws IllegalArgumentException if cmt is null
     62     * @throws IllegalArgumentException if the same primitive was already stored with a higher or equal version
    5963     */
    6064    public void put(HistoryOsmPrimitive primitive, ChangesetModificationType cmt) {
    6165        CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
    6266        CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
    63         primitives.put(primitive.getPrimitiveId(), primitive);
    64         modificationTypes.put(primitive.getPrimitiveId(), cmt);
     67        DefaultChangesetDataSetEntry csEntry = new DefaultChangesetDataSetEntry(cmt, primitive);
     68        Object val = entryMap.get(primitive.getPrimitiveId());
     69        ChangesetDataSetEntry[] entries;
     70        if (val == null) {
     71            entryMap.put(primitive.getPrimitiveId(), csEntry);
     72            return;
     73        }
     74        if (val instanceof ChangesetDataSetEntry) {
     75            entries = new ChangesetDataSetEntry[2];
     76            entries[0] = (ChangesetDataSetEntry) val;
     77            if (primitive.getVersion() <= entries[0].getPrimitive().getVersion()) {
     78                throw new IllegalArgumentException(
     79                        tr("Changeset {0}: Unexpected order of versions for {1}: v{2} is not higher than v{3}",
     80                                String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
     81                                primitive.getVersion(), entries[0].getPrimitive().getVersion()));
     82            }
     83        } else {
     84            entries = (ChangesetDataSetEntry[]) val;
     85        }
     86        if (entries[1] != null) {
     87            Logging.info("Changeset {0}: Change of {1} v{2} is replaced by version v{3}",
     88                    String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
     89                    entries[1].getPrimitive().getVersion(), primitive.getVersion());
     90        }
     91        entries[1] = csEntry;
     92        entryMap.put(primitive.getPrimitiveId(), entries);
    6593    }
    6694
    6795    /**
     
    7199     */
    72100    public boolean contains(PrimitiveId id) {
    73101        if (id == null) return false;
    74         return primitives.containsKey(id);
     102        return entryMap.containsKey(id);
    75103    }
    76104
    77105    /**
    78      * Replies the modification type for the object with id <code>id</code>. Replies null, if id is null or
     106     * Replies the last modification type for the object with id <code>id</code>. Replies null, if id is null or
    79107     * if the object with id <code>id</code> isn't in the changeset content.
    80108     *
    81109     * @param id the id
    82      * @return the modification type
     110     * @return the last modification type or null
    83111     */
    84112    public ChangesetModificationType getModificationType(PrimitiveId id) {
    85         if (!contains(id)) return null;
    86         return modificationTypes.get(id);
     113        ChangesetDataSetEntry e = getLastEntry(id);
     114        return e != null ? e.getModificationType() : null;
    87115    }
    88116
    89117    /**
    90118     * Replies true if the primitive with id <code>id</code> was created in this
    91      * changeset. Replies false, if id is null.
     119     * changeset. Replies false, if id is null or not in the dataset.
    92120     *
    93121     * @param id the id
    94122     * @return true if the primitive with id <code>id</code> was created in this
     
    95123     * changeset.
    96124     */
    97125    public boolean isCreated(PrimitiveId id) {
    98         if (!contains(id)) return false;
    99         return ChangesetModificationType.CREATED == getModificationType(id);
     126        ChangesetDataSetEntry e = getFirstEntry(id);
     127        return e != null && e.getModificationType() == ChangesetModificationType.CREATED;
    100128    }
    101129
    102130    /**
    103131     * Replies true if the primitive with id <code>id</code> was updated in this
    104      * changeset. Replies false, if id is null.
     132     * changeset. Replies false, if id is null or not in the dataset.
    105133     *
    106134     * @param id the id
    107135     * @return true if the primitive with id <code>id</code> was updated in this
     
    108136     * changeset.
    109137     */
    110138    public boolean isUpdated(PrimitiveId id) {
    111         if (!contains(id)) return false;
    112         return ChangesetModificationType.UPDATED == getModificationType(id);
     139        ChangesetDataSetEntry e = getLastEntry(id);
     140        return e != null && e.getModificationType() == ChangesetModificationType.UPDATED;
    113141    }
    114142
    115143    /**
    116144     * Replies true if the primitive with id <code>id</code> was deleted in this
    117      * changeset. Replies false, if id is null.
     145     * changeset. Replies false, if id is null or not in the dataset.
    118146     *
    119147     * @param id the id
    120148     * @return true if the primitive with id <code>id</code> was deleted in this
     
    121149     * changeset.
    122150     */
    123151    public boolean isDeleted(PrimitiveId id) {
    124         if (!contains(id)) return false;
    125         return ChangesetModificationType.DELETED == getModificationType(id);
     152        ChangesetDataSetEntry e = getLastEntry(id);
     153        return e != null && e.getModificationType() == ChangesetModificationType.DELETED;
    126154    }
    127155
    128156    /**
    129      * Replies the set of primitives with a specific modification type
     157     * Replies the number of primitives in the dataset.
    130158     *
    131      * @param cmt the modification type. Must not be null.
    132      * @return the set of primitives
    133      * @throws IllegalArgumentException if cmt is null
     159     * @return the number of primitives in the dataset.
    134160     */
    135     public Set<HistoryOsmPrimitive> getPrimitivesByModificationType(ChangesetModificationType cmt) {
    136         CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
    137         return modificationTypes.entrySet().stream()
    138                 .filter(entry -> entry.getValue() == cmt)
    139                 .map(entry -> primitives.get(entry.getKey()))
    140                 .collect(Collectors.toSet());
     161    public int size() {
     162        return entryMap.size();
    141163    }
    142164
    143165    /**
    144      * Replies the number of objects in the dataset
     166     * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
     167     * null, if there is no such primitive in the data set. If the primitive was modified
     168     * multiple times, the last version is returned.
    145169     *
    146      * @return the number of objects in the dataset
     170     * @param id the id
     171     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
    147172     */
    148     public int size() {
    149         return primitives.size();
     173    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
     174        ChangesetDataSetEntry e = getLastEntry(id);
     175        return e != null ? e.getPrimitive() : null;
    150176    }
    151177
    152178    /**
    153      * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
     179     * @return an unmodifiable set of all primitives in this dataset.
     180     * @since xxx
     181     */
     182    public Set<PrimitiveId> getIds() {
     183        return Collections.unmodifiableSet(entryMap.keySet());
     184    }
     185
     186    /**
     187     * Replies the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
    154188     * null, if there is no such primitive in the data set.
    155      *
    156189     * @param id the id
    157      * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
     190     * @return the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
     191     * @since xxx
    158192     */
    159     public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
    160         if (id == null) return null;
    161         return primitives.get(id);
     193    public ChangesetDataSetEntry getFirstEntry(PrimitiveId id) {
     194        if (id == null)
     195            return null;
     196        Object val = entryMap.get(id);
     197        if (val == null)
     198            return null;
     199        if (val instanceof ChangesetDataSetEntry[]) {
     200            ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
     201            return entries[0];
     202        } else {
     203            return (ChangesetDataSetEntry) val;
     204        }
    162205    }
    163206
    164207    /**
    165      * Returns an iterator over dataset entries.
    166      * @return an iterator over dataset entries
     208     * Replies the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
     209     * null, if there is no such primitive in the data set.
     210     * @param id the id
     211     * @return the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
     212     * @since xxx
    167213     */
     214    public ChangesetDataSetEntry getLastEntry(PrimitiveId id) {
     215        if (id == null)
     216            return null;
     217        Object val = entryMap.get(id);
     218        if (val == null)
     219            return null;
     220        if (val instanceof ChangesetDataSetEntry[]) {
     221            ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
     222            return entries[1];
     223        } else {
     224            return (ChangesetDataSetEntry) val;
     225        }
     226    }
     227
     228    /**
     229     * Returns an iterator over dataset entries. The elements are returned in no particular order.
     230     * @return an iterator over dataset entries. If a primitive was changed multiple times, only the last entry is returned.
     231     */
    168232    public Iterator<ChangesetDataSetEntry> iterator() {
    169233        return new DefaultIterator();
    170234    }
    171235
    172     private static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
     236    /**
     237     * Class to keep one entry of a changeset: the combination of modification type and primitive.
     238     */
     239    public static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
    173240        private final ChangesetModificationType modificationType;
    174241        private final HistoryOsmPrimitive primitive;
    175242
    176         DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
     243        /**
     244         * Construct new entry.
     245         * @param modificationType the modification type
     246         * @param primitive the primitive
     247         */
     248        public DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
    177249            this.modificationType = modificationType;
    178250            this.primitive = primitive;
    179251        }
     
    187259        public HistoryOsmPrimitive getPrimitive() {
    188260            return primitive;
    189261        }
     262
     263        @Override
     264        public String toString() {
     265            return modificationType.toString() + " " + primitive.toString();
     266        }
    190267    }
    191268
    192269    private class DefaultIterator implements Iterator<ChangesetDataSetEntry> {
    193         private final Iterator<Entry<PrimitiveId, ChangesetModificationType>> typeIterator;
     270        private final Iterator<Entry<PrimitiveId, Object>> typeIterator;
    194271
    195272        DefaultIterator() {
    196             typeIterator = modificationTypes.entrySet().iterator();
     273            typeIterator = entryMap.entrySet().iterator();
    197274        }
    198275
    199276        @Override
     
    203280
    204281        @Override
    205282        public ChangesetDataSetEntry next() {
    206             Entry<PrimitiveId, ChangesetModificationType> next = typeIterator.next();
    207             return new DefaultChangesetDataSetEntry(next.getValue(), primitives.get(next.getKey()));
     283            Entry<PrimitiveId, Object> next = typeIterator.next();
     284            // get last entry
     285            Object val = next.getValue();
     286            ChangesetDataSetEntry last;
     287            if (val instanceof ChangesetDataSetEntry[]) {
     288                ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
     289                last = entries[1];
     290            } else {
     291                last = (ChangesetDataSetEntry) val;
     292            }
     293            return new DefaultChangesetDataSetEntry(last.getModificationType(), last.getPrimitive());
    208294        }
    209295
    210296        @Override
  • src/org/openstreetmap/josm/io/AbstractParser.java

     
    2828    /** the current primitive to be read */
    2929    protected HistoryOsmPrimitive currentPrimitive;
    3030    protected Locator locator;
     31    /** if true, replace user information in input by anonymous user */
     32    protected boolean useAnonymousUser;
    3133
    3234    @Override
    3335    public void setDocumentLocator(Locator locator) {
     
    111113        long changesetId = changeset != null ? changeset : 0L;
    112114        boolean visible = getMandatoryAttributeBoolean(atts, "visible");
    113115
    114         Long uid = getAttributeLong(atts, "uid");
    115         String userStr = atts.getValue("user");
    116         User user;
    117         if (userStr != null) {
    118             if (uid != null) {
    119                 user = User.createOsmUser(uid, userStr);
    120                 user.setPreferredName(userStr);
    121             } else {
    122                 user = User.createLocalUser(userStr);
     116        User user = null;
     117        if (!useAnonymousUser) {
     118            Long uid = getAttributeLong(atts, "uid");
     119            String userStr = atts.getValue("user");
     120            if (userStr != null) {
     121                if (uid != null) {
     122                    user = User.createOsmUser(uid, userStr);
     123                    user.setPreferredName(userStr);
     124                } else {
     125                    user = User.createLocalUser(userStr);
     126                }
    123127            }
    124         } else {
     128        }
     129        if (user == null) {
    125130            user = User.getAnonymous();
    126131        }
    127132
  • src/org/openstreetmap/josm/io/OsmChangesetContentParser.java

     
    3434    private final ChangesetDataSet data = new ChangesetDataSet();
    3535
    3636    private class Parser extends AbstractParser {
     37        Parser(boolean useAnonymousUser) {
     38            this.useAnonymousUser = useAnonymousUser;
     39        }
    3740
    3841        /** the current change modification type */
    3942        private ChangesetDataSet.ChangesetModificationType currentModificationType;
     
    120123     * @param source the input stream with the changeset content as XML document. Must not be null.
    121124     * @throws IllegalArgumentException if source is {@code null}.
    122125     */
    123     @SuppressWarnings("resource")
    124126    public OsmChangesetContentParser(InputStream source) {
    125127        CheckParameterUtil.ensureParameterNotNull(source, "source");
    126128        this.source = new InputSource(new InputStreamReader(source, StandardCharsets.UTF_8));
     
    146148     * exceptions.
    147149     */
    148150    public ChangesetDataSet parse(ProgressMonitor progressMonitor) throws XmlParsingException {
     151        return parse(progressMonitor, false);
     152    }
     153
     154    /**
     155     * Parses the content.
     156     *
     157     * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
     158     * @param useAnonymousUser if true, replace all user information with the anonymous user
     159     * @return the parsed data
     160     * @throws XmlParsingException if something went wrong. Check for chained
     161     * exceptions.
     162     */
     163    public ChangesetDataSet parse(ProgressMonitor progressMonitor, boolean useAnonymousUser) throws XmlParsingException {
    149164        if (progressMonitor == null) {
    150165            progressMonitor = NullProgressMonitor.INSTANCE;
    151166        }
     
    152167        try {
    153168            progressMonitor.beginTask("");
    154169            progressMonitor.indeterminateSubTask(tr("Parsing changeset content ..."));
    155             XmlUtils.parseSafeSAX(source, new Parser());
     170            XmlUtils.parseSafeSAX(source, new Parser(useAnonymousUser));
    156171        } catch (XmlParsingException e) {
    157172            throw e;
    158173        } catch (ParserConfigurationException | SAXException | IOException e) {
     
    171186     * exceptions.
    172187     */
    173188    public ChangesetDataSet parse() throws XmlParsingException {
    174         return parse(null);
     189        return parse(null, false);
    175190    }
    176191}
  • src/org/openstreetmap/josm/io/OsmServerChangesetReader.java

     
    2626 *
    2727 */
    2828public class OsmServerChangesetReader extends OsmServerReader {
     29    final boolean useAnonymousUser;
    2930
    3031    /**
     32     * Constructs a new {@code OsmServerChangesetReader} with default settings.
     33     */
     34    public OsmServerChangesetReader() {
     35        this(false);
     36    }
     37
     38    /**
     39     * Constructs a new {@code OsmServerChangesetReader}
     40     * @param useAnonymousUser if true, replace all user information with the anonymous user
     41     * @since xxx
     42     */
     43    public OsmServerChangesetReader(boolean useAnonymousUser) {
     44        super();
     45        this.useAnonymousUser = useAnonymousUser;
     46    }
     47
     48    /**
    3149     * don't use - not implemented!
    3250     */
    3351    @Override
     
    198216                    return null;
    199217                monitor.setCustomText(tr("Downloading content for changeset {0} ...", id));
    200218                OsmChangesetContentParser parser = new OsmChangesetContentParser(in);
    201                 result = parser.parse(monitor.createSubTaskMonitor(1, true));
     219                result = parser.parse(monitor.createSubTaskMonitor(1, true), useAnonymousUser);
    202220            } catch (IOException e) {
    203221                Logging.warn(e);
    204222            }
  • test/unit/org/openstreetmap/josm/data/osm/ChangesetDataSetTest.java

     
    88
    99import java.util.Date;
    1010import java.util.Iterator;
    11 import java.util.Set;
    1211
    1312import org.junit.Rule;
    1413import org.junit.Test;
     
    1716import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetDataSetEntry;
    1817import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType;
    1918import org.openstreetmap.josm.data.osm.history.HistoryNode;
    20 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
    2119import org.openstreetmap.josm.testutils.JOSMTestRules;
    2220import org.openstreetmap.josm.tools.Logging;
    2321
     
    3634    public JOSMTestRules test = new JOSMTestRules();
    3735
    3836    /**
    39      * Unit test of method {@link ChangesetDataSet#getPrimitivesByModificationType}.
    40      */
    41     @Test
    42     public void testGetPrimitivesByModificationType() {
    43         final ChangesetDataSet cds = new ChangesetDataSet();
    44         // empty object, null parameter => IllegalArgumentException
    45         try {
    46             cds.getPrimitivesByModificationType(null);
    47             fail("Should have thrown an IllegalArgumentException as we gave a null argument.");
    48         } catch (IllegalArgumentException e) {
    49             Logging.trace(e);
    50             // Was expected
    51         }
    52 
    53         // empty object, a modification type => empty list
    54         assertTrue(
    55             "Empty data set should produce an empty list.",
    56             cds.getPrimitivesByModificationType(
    57                     ChangesetModificationType.CREATED).isEmpty()
    58         );
    59 
    60         // object with various items and modification types, fetch for CREATED
    61         // => list containing only the CREATED item
    62         HistoryNode prim1 = new HistoryNode(1, 1, true, User.getAnonymous(), 1, new Date(), LatLon.ZERO);
    63         HistoryNode prim2 = new HistoryNode(2, 1, true, User.createLocalUser("test"), 1, new Date(), LatLon.NORTH_POLE);
    64         HistoryNode prim3 = new HistoryNode(3, 1, true, User.getAnonymous(), 1, new Date(), LatLon.SOUTH_POLE);
    65         cds.put(prim1, ChangesetModificationType.CREATED);
    66         cds.put(prim2, ChangesetModificationType.DELETED);
    67         cds.put(prim3, ChangesetModificationType.UPDATED);
    68         Set<HistoryOsmPrimitive> result = cds.getPrimitivesByModificationType(
    69                     ChangesetModificationType.CREATED);
    70         assertEquals("We should have found only one item.", 1, result.size());
    71         assertTrue("The item found is prim1.", result.contains(prim1));
    72     }
    73 
    74     /**
    7537     * Unit test of method {@link ChangesetDataSet#iterator}.
    7638     */
    7739    @Test