Changeset 14946 in josm


Ignore:
Timestamp:
2019-04-01T07:46:58+02:00 (9 months ago)
Author:
GerdP
Message:

see #17459: remove duplicated code in reverter corehacks
Step 1: Add needed code to core methods.
Major changes:
1) If a changeset contains multiple versions for a primitive, class ChangesetDataSet keeps the first and the last version (not just the last) and provides methods to access them, unused method getPrimitivesByModificationType() was removed
2) The changeset reader classes have a new field useAnonymousUser. If set, the user found in a changeset is replaced by the anonymous user.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java

    r14214 r14946  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.osm;
    3 
     3import static org.openstreetmap.josm.tools.I18n.tr;
     4
     5import java.util.Collections;
    46import java.util.HashMap;
    57import java.util.Iterator;
     
    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 {
     
    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    /**
     
    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
     
    72100    public boolean contains(PrimitiveId id) {
    73101        if (id == null) return false;
    74         return primitives.containsKey(id);
    75     }
    76 
    77     /**
    78      * Replies the modification type for the object with id <code>id</code>. Replies null, if id is null or
     102        return entryMap.containsKey(id);
     103    }
     104
     105    /**
     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
     
    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
     
    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
     
    122150     */
    123151    public boolean isDeleted(PrimitiveId id) {
    124         if (!contains(id)) return false;
    125         return ChangesetModificationType.DELETED == getModificationType(id);
    126     }
    127 
    128     /**
    129      * Replies the set of primitives with a specific modification type
    130      *
    131      * @param cmt the modification type. Must not be null.
    132      * @return the set of primitives
    133      * @throws IllegalArgumentException if cmt is null
    134      */
    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());
    141     }
    142 
    143     /**
    144      * Replies the number of objects in the dataset
    145      *
    146      * @return the number of objects in the dataset
     152        ChangesetDataSetEntry e = getLastEntry(id);
     153        return e != null && e.getModificationType() == ChangesetModificationType.DELETED;
     154    }
     155
     156    /**
     157     * Replies the number of primitives in the dataset.
     158     *
     159     * @return the number of primitives in the dataset.
    147160     */
    148161    public int size() {
    149         return primitives.size();
     162        return entryMap.size();
    150163    }
    151164
    152165    /**
    153166     * 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.
     169     *
     170     * @param id the id
     171     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
     172     */
     173    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
     174        ChangesetDataSetEntry e = getLastEntry(id);
     175        return e != null ? e.getPrimitive() : null;
     176    }
     177
     178    /**
     179     * @return an unmodifiable set of all primitives in this dataset.
     180     * @since 14946
     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      *
    156      * @param id the id
    157      * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
    158      */
    159     public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
    160         if (id == null) return null;
    161         return primitives.get(id);
    162     }
    163 
    164     /**
    165      * Returns an iterator over dataset entries.
    166      * @return an iterator over dataset entries
     189     * @param id the id
     190     * @return the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
     191     * @since 14946
     192     */
     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        }
     205    }
     206
     207    /**
     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 14946
     213     */
     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.
    167231     */
    168232    public Iterator<ChangesetDataSetEntry> iterator() {
     
    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;
     
    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
     
    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
  • trunk/src/org/openstreetmap/josm/io/AbstractParser.java

    r14214 r14946  
    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
     
    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        }
  • trunk/src/org/openstreetmap/josm/io/OsmChangesetContentParser.java

    r13901 r14946  
    3535
    3636    private class Parser extends AbstractParser {
     37        Parser(boolean useAnonymousUser) {
     38            this.useAnonymousUser = useAnonymousUser;
     39        }
    3740
    3841        /** the current change modification type */
     
    121124     * @throws IllegalArgumentException if source is {@code null}.
    122125     */
    123     @SuppressWarnings("resource")
    124126    public OsmChangesetContentParser(InputStream source) {
    125127        CheckParameterUtil.ensureParameterNotNull(source, "source");
     
    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     * @since 14946
     163     */
     164    public ChangesetDataSet parse(ProgressMonitor progressMonitor, boolean useAnonymousUser) throws XmlParsingException {
    149165        if (progressMonitor == null) {
    150166            progressMonitor = NullProgressMonitor.INSTANCE;
     
    153169            progressMonitor.beginTask("");
    154170            progressMonitor.indeterminateSubTask(tr("Parsing changeset content ..."));
    155             XmlUtils.parseSafeSAX(source, new Parser());
     171            XmlUtils.parseSafeSAX(source, new Parser(useAnonymousUser));
    156172        } catch (XmlParsingException e) {
    157173            throw e;
     
    172188     */
    173189    public ChangesetDataSet parse() throws XmlParsingException {
    174         return parse(null);
     190        return parse(null, false);
    175191    }
    176192}
  • trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java

    r13761 r14946  
    2727 */
    2828public class OsmServerChangesetReader extends OsmServerReader {
     29    final boolean useAnonymousUser;
     30
     31    /**
     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 14946
     42     */
     43    public OsmServerChangesetReader(boolean useAnonymousUser) {
     44        super();
     45        this.useAnonymousUser = useAnonymousUser;
     46    }
    2947
    3048    /**
     
    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);
  • trunk/test/unit/org/openstreetmap/josm/data/osm/ChangesetDataSetTest.java

    r12620 r14946  
    99import java.util.Date;
    1010import java.util.Iterator;
    11 import java.util.Set;
    1211
    1312import org.junit.Rule;
     
    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;
     
    3533    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    3634    public JOSMTestRules test = new JOSMTestRules();
    37 
    38     /**
    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     }
    7335
    7436    /**
Note: See TracChangeset for help on using the changeset viewer.