Ticket #17459: add-reverter-core-hacks-v3.patch
File add-reverter-core-hacks-v3.patch, 21.7 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.osm; 3 import static org.openstreetmap.josm.tools.I18n.tr; 3 4 5 import java.util.Collections; 4 6 import java.util.HashMap; 5 7 import java.util.Iterator; 6 8 import java.util.Map; 7 9 import java.util.Map.Entry; 8 10 import java.util.Set; 9 import java.util.stream.Collectors;10 11 11 12 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive; 12 13 import org.openstreetmap.josm.tools.CheckParameterUtil; 14 import org.openstreetmap.josm.tools.Logging; 13 15 14 16 /** 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. 16 19 */ 17 20 public class ChangesetDataSet { 18 21 … … 46 49 HistoryOsmPrimitive getPrimitive(); 47 50 } 48 51 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<>(); 51 54 52 55 /** 53 56 * Remembers a history primitive with the given modification type … … 56 59 * @param cmt the modification type. Must not be null. 57 60 * @throws IllegalArgumentException if primitive is null 58 61 * @throws IllegalArgumentException if cmt is null 62 * @throws IllegalArgumentException if the same primitive was already stored with a higher or equal version 59 63 */ 60 64 public void put(HistoryOsmPrimitive primitive, ChangesetModificationType cmt) { 61 65 CheckParameterUtil.ensureParameterNotNull(primitive, "primitive"); 62 66 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); 65 93 } 66 94 67 95 /** … … 71 99 */ 72 100 public boolean contains(PrimitiveId id) { 73 101 if (id == null) return false; 74 return primitives.containsKey(id);102 return entryMap.containsKey(id); 75 103 } 76 104 77 105 /** 78 * Replies the modification type for the object with id <code>id</code>. Replies null, if id is null or106 * Replies the last modification type for the object with id <code>id</code>. Replies null, if id is null or 79 107 * if the object with id <code>id</code> isn't in the changeset content. 80 108 * 81 109 * @param id the id 82 * @return the modification type110 * @return the last modification type or null 83 111 */ 84 112 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; 87 115 } 88 116 89 117 /** 90 118 * 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. 92 120 * 93 121 * @param id the id 94 122 * @return true if the primitive with id <code>id</code> was created in this … … 95 123 * changeset. 96 124 */ 97 125 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; 100 128 } 101 129 102 130 /** 103 131 * 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. 105 133 * 106 134 * @param id the id 107 135 * @return true if the primitive with id <code>id</code> was updated in this … … 108 136 * changeset. 109 137 */ 110 138 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; 113 141 } 114 142 115 143 /** 116 144 * 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. 118 146 * 119 147 * @param id the id 120 148 * @return true if the primitive with id <code>id</code> was deleted in this … … 121 149 * changeset. 122 150 */ 123 151 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; 126 154 } 127 155 128 156 /** 129 * Replies the set of primitives with a specific modification type157 * Replies the number of primitives in the dataset. 130 158 * 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. 134 160 */ 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(); 141 163 } 142 164 143 165 /** 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. 145 169 * 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 147 172 */ 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; 150 176 } 151 177 152 178 /** 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. 154 188 * null, if there is no such primitive in the data set. 155 *156 189 * @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 158 192 */ 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 } 162 205 } 163 206 164 207 /** 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 167 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. 231 */ 168 232 public Iterator<ChangesetDataSetEntry> iterator() { 169 233 return new DefaultIterator(); 170 234 } 171 235 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 { 173 240 private final ChangesetModificationType modificationType; 174 241 private final HistoryOsmPrimitive primitive; 175 242 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) { 177 249 this.modificationType = modificationType; 178 250 this.primitive = primitive; 179 251 } … … 187 259 public HistoryOsmPrimitive getPrimitive() { 188 260 return primitive; 189 261 } 262 263 @Override 264 public String toString() { 265 return modificationType.toString() + " " + primitive.toString(); 266 } 190 267 } 191 268 192 269 private class DefaultIterator implements Iterator<ChangesetDataSetEntry> { 193 private final Iterator<Entry<PrimitiveId, ChangesetModificationType>> typeIterator;270 private final Iterator<Entry<PrimitiveId, Object>> typeIterator; 194 271 195 272 DefaultIterator() { 196 typeIterator = modificationTypes.entrySet().iterator();273 typeIterator = entryMap.entrySet().iterator(); 197 274 } 198 275 199 276 @Override … … 203 280 204 281 @Override 205 282 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()); 208 294 } 209 295 210 296 @Override -
src/org/openstreetmap/josm/io/AbstractParser.java
28 28 /** the current primitive to be read */ 29 29 protected HistoryOsmPrimitive currentPrimitive; 30 30 protected Locator locator; 31 /** if true, replace user information in input by anonymous user */ 32 protected boolean useAnonymousUser; 31 33 32 34 @Override 33 35 public void setDocumentLocator(Locator locator) { … … 111 113 long changesetId = changeset != null ? changeset : 0L; 112 114 boolean visible = getMandatoryAttributeBoolean(atts, "visible"); 113 115 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 } 123 127 } 124 } else { 128 } 129 if (user == null) { 125 130 user = User.getAnonymous(); 126 131 } 127 132 -
src/org/openstreetmap/josm/io/OsmChangesetContentParser.java
34 34 private final ChangesetDataSet data = new ChangesetDataSet(); 35 35 36 36 private class Parser extends AbstractParser { 37 Parser(boolean useAnonymousUser) { 38 this.useAnonymousUser = useAnonymousUser; 39 } 37 40 38 41 /** the current change modification type */ 39 42 private ChangesetDataSet.ChangesetModificationType currentModificationType; … … 120 123 * @param source the input stream with the changeset content as XML document. Must not be null. 121 124 * @throws IllegalArgumentException if source is {@code null}. 122 125 */ 123 @SuppressWarnings("resource")124 126 public OsmChangesetContentParser(InputStream source) { 125 127 CheckParameterUtil.ensureParameterNotNull(source, "source"); 126 128 this.source = new InputSource(new InputStreamReader(source, StandardCharsets.UTF_8)); … … 146 148 * exceptions. 147 149 */ 148 150 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 { 149 164 if (progressMonitor == null) { 150 165 progressMonitor = NullProgressMonitor.INSTANCE; 151 166 } … … 152 167 try { 153 168 progressMonitor.beginTask(""); 154 169 progressMonitor.indeterminateSubTask(tr("Parsing changeset content ...")); 155 XmlUtils.parseSafeSAX(source, new Parser( ));170 XmlUtils.parseSafeSAX(source, new Parser(useAnonymousUser)); 156 171 } catch (XmlParsingException e) { 157 172 throw e; 158 173 } catch (ParserConfigurationException | SAXException | IOException e) { … … 171 186 * exceptions. 172 187 */ 173 188 public ChangesetDataSet parse() throws XmlParsingException { 174 return parse(null );189 return parse(null, false); 175 190 } 176 191 } -
src/org/openstreetmap/josm/io/OsmServerChangesetReader.java
26 26 * 27 27 */ 28 28 public class OsmServerChangesetReader extends OsmServerReader { 29 final boolean useAnonymousUser; 29 30 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 xxx 42 */ 43 public OsmServerChangesetReader(boolean useAnonymousUser) { 44 super(); 45 this.useAnonymousUser = useAnonymousUser; 46 } 47 48 /** 31 49 * don't use - not implemented! 32 50 */ 33 51 @Override … … 198 216 return null; 199 217 monitor.setCustomText(tr("Downloading content for changeset {0} ...", id)); 200 218 OsmChangesetContentParser parser = new OsmChangesetContentParser(in); 201 result = parser.parse(monitor.createSubTaskMonitor(1, true) );219 result = parser.parse(monitor.createSubTaskMonitor(1, true), useAnonymousUser); 202 220 } catch (IOException e) { 203 221 Logging.warn(e); 204 222 } -
test/unit/org/openstreetmap/josm/data/osm/ChangesetDataSetTest.java
8 8 9 9 import java.util.Date; 10 10 import java.util.Iterator; 11 import java.util.Set;12 11 13 12 import org.junit.Rule; 14 13 import org.junit.Test; … … 17 16 import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetDataSetEntry; 18 17 import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType; 19 18 import org.openstreetmap.josm.data.osm.history.HistoryNode; 20 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;21 19 import org.openstreetmap.josm.testutils.JOSMTestRules; 22 20 import org.openstreetmap.josm.tools.Logging; 23 21 … … 36 34 public JOSMTestRules test = new JOSMTestRules(); 37 35 38 36 /** 39 * Unit test of method {@link ChangesetDataSet#getPrimitivesByModificationType}.40 */41 @Test42 public void testGetPrimitivesByModificationType() {43 final ChangesetDataSet cds = new ChangesetDataSet();44 // empty object, null parameter => IllegalArgumentException45 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 expected51 }52 53 // empty object, a modification type => empty list54 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 CREATED61 // => list containing only the CREATED item62 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 /**75 37 * Unit test of method {@link ChangesetDataSet#iterator}. 76 38 */ 77 39 @Test