Changeset 11606 in josm
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java
r10791 r11606 476 476 477 477 final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives); 478 TagConflictResolutionUtil. combineTigerTags(completeWayTags);478 TagConflictResolutionUtil.applyAutomaticTagConflictResolution(completeWayTags); 479 479 TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives); 480 480 final TagCollection tagsToEdit = new TagCollection(completeWayTags); -
trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolutionUtil.java
r8855 r11606 3 3 4 4 import java.util.ArrayList; 5 import java.util.Arrays; 5 6 import java.util.Collection; 6 7 import java.util.Collections; 8 import java.util.HashMap; 9 import java.util.LinkedHashSet; 10 import java.util.List; 11 import java.util.Set; 12 import java.util.TreeSet; 13 import java.util.regex.Pattern; 14 import java.util.regex.PatternSyntaxException; 15 import java.util.stream.Collectors; 16 17 import org.openstreetmap.josm.Main; 18 import org.openstreetmap.josm.data.Preferences.pref; 7 19 import org.openstreetmap.josm.data.osm.OsmPrimitive; 8 20 import org.openstreetmap.josm.data.osm.Tag; 9 21 import org.openstreetmap.josm.data.osm.TagCollection; 10 import org.openstreetmap.josm. data.osm.TigerUtils;22 import org.openstreetmap.josm.tools.Pair; 11 23 12 24 /** … … 15 27 */ 16 28 public final class TagConflictResolutionUtil { 29 30 /** The OSM key 'source' */ 31 private static final String KEY_SOURCE = "source"; 32 33 /** The group identifier for French Cadastre choices */ 34 private static final String GRP_FR_CADASTRE = "FR:cadastre"; 35 36 /** The group identifier for Canadian CANVEC choices */ 37 private static final String GRP_CA_CANVEC = "CA:canvec"; 38 39 /** 40 * Default preferences for the list of AutomaticCombine tag conflict resolvers. 41 */ 42 private static final Collection<AutomaticCombine> defaultAutomaticTagConflictCombines = Arrays.asList( 43 new AutomaticCombine("tiger:tlid", "US TIGER tlid", false, ":", "Integer"), 44 new AutomaticCombine("tiger:(?!tlid$).*", "US TIGER not tlid", true, ":", "String") 45 ); 46 47 /** 48 * Default preferences for the list of AutomaticChoice tag conflict resolvers. 49 */ 50 private static final Collection<AutomaticChoice> defaultAutomaticTagConflictChoices = Arrays.asList( 51 /* "source" "FR:cadastre" - https://wiki.openstreetmap.org/wiki/FR:WikiProject_France/Cadastre 52 * List of choices for the "source" tag of data exported from the French cadastre, 53 * which ends by the exported year generating many conflicts. 54 * The generated score begins with the year number to select the most recent one. 55 */ 56 new AutomaticChoice(KEY_SOURCE, GRP_FR_CADASTRE, "FR cadastre source, manual value", true, 57 "cadastre", "0"), 58 new AutomaticChoice(KEY_SOURCE, GRP_FR_CADASTRE, "FR cadastre source, initial format", true, 59 "extraction vectorielle v1 cadastre-dgi-fr source : Direction G[eé]n[eé]rale des Imp[oô]ts" 60 + " - Cadas\\. Mise [aà] jour : (2[0-9]{3})", 61 "$1 1"), 62 new AutomaticChoice(KEY_SOURCE, GRP_FR_CADASTRE, "FR cadastre source, last format", true, 63 "(?:cadastre-dgi-fr source : )?Direction G[eé]n[eé]rale des (?:Imp[oô]ts|Finances Publiques)" 64 + " - Cadas(?:tre)?(?:\\.| ;) [Mm]ise [aà] jour : (2[0-9]{3})", 65 "$1 2"), 66 /* "source" "CA:canvec" - https://wiki.openstreetmap.org/wiki/CanVec 67 * List of choices for the "source" tag of data exported from Natural Resources Canada (NRCan) 68 */ 69 new AutomaticChoice(KEY_SOURCE, GRP_CA_CANVEC, "CA canvec source, initial value", true, 70 "CanVec_Import_2009", "00"), 71 new AutomaticChoice(KEY_SOURCE, GRP_CA_CANVEC, "CA canvec source, 4.0/6.0 value", true, 72 "CanVec ([1-9]).0 - NRCan", "0$1"), 73 new AutomaticChoice(KEY_SOURCE, GRP_CA_CANVEC, "CA canvec source, 7.0/8.0 value", true, 74 "NRCan-CanVec-([1-9]).0", "0$1"), 75 new AutomaticChoice(KEY_SOURCE, GRP_CA_CANVEC, "CA canvec source, 10.0/12.0 value", true, 76 "NRCan-CanVec-(1[012]).0", "$1") 77 ); 78 79 private static volatile Collection<AutomaticTagConflictResolver> automaticTagConflictResolvers; 17 80 18 81 private TagConflictResolutionUtil() { … … 61 124 62 125 /** 63 * Combines tags from TIGER data64 *65 * @param tc the tag collection66 */67 public static void combineTigerTags(TagCollection tc) {68 for (String key: tc.getKeys()) {69 if (TigerUtils.isTigerTag(key)) {70 tc.setUniqueForKey(key, TigerUtils.combineTags(tc.getValues(key)));71 }72 }73 }74 75 /**76 126 * Completes tags in the tag collection <code>tc</code> with the empty value 77 127 * for each tag. If the empty value is present the tag conflict resolution dialog … … 89 139 } 90 140 } 141 142 /** 143 * Automatically resolve some tag conflicts. 144 * The list of automatic resolution is taken from the preferences. 145 * @param tc the tag collection 146 * @since 11606 147 */ 148 public static void applyAutomaticTagConflictResolution(TagCollection tc) { 149 applyAutomaticTagConflictResolution(tc, getAutomaticTagConflictResolvers()); 150 } 151 152 /** 153 * Get the AutomaticTagConflictResolvers configured in the Preferences or the default ones. 154 * @return the configured AutomaticTagConflictResolvers. 155 * @since 11606 156 */ 157 public static Collection<AutomaticTagConflictResolver> getAutomaticTagConflictResolvers() { 158 if (automaticTagConflictResolvers == null) { 159 Collection<AutomaticCombine> automaticTagConflictCombines = 160 Main.pref.getListOfStructs( 161 "automatic-tag-conflict-resolution.combine", 162 defaultAutomaticTagConflictCombines, AutomaticCombine.class); 163 Collection<AutomaticChoiceGroup> automaticTagConflictChoiceGroups = 164 AutomaticChoiceGroup.groupChoices(Main.pref.getListOfStructs( 165 "automatic-tag-conflict-resolution.choice", 166 defaultAutomaticTagConflictChoices, AutomaticChoice.class)); 167 // Use a tmp variable to fully construct the collection before setting 168 // the volatile variable automaticTagConflictResolvers. 169 ArrayList<AutomaticTagConflictResolver> tmp = new ArrayList<>(); 170 tmp.addAll(automaticTagConflictCombines); 171 tmp.addAll(automaticTagConflictChoiceGroups); 172 automaticTagConflictResolvers = tmp; 173 } 174 return Collections.unmodifiableCollection(automaticTagConflictResolvers); 175 } 176 177 /** 178 * An automatic tag conflict resolver interface. 179 * @since 11606 180 */ 181 interface AutomaticTagConflictResolver { 182 /** 183 * Check if this resolution apply to the given Tag key. 184 * @param key The Tag key to match. 185 * @return true if this automatic resolution apply to the given Tag key. 186 */ 187 boolean matchesKey(String key); 188 189 /** 190 * Try to resolve a conflict between a set of values for a Tag 191 * @param values the set of conflicting values for the Tag. 192 * @return the resolved value or null if resolution was not possible. 193 */ 194 String resolve(Set<String> values); 195 } 196 197 /** 198 * Automatically resolve some given conflicts using the given resolvers. 199 * @param tc the tag collection. 200 * @param resolvers the list of automatic tag conflict resolvers to apply. 201 * @since 11606 202 */ 203 public static void applyAutomaticTagConflictResolution(TagCollection tc, 204 Collection<AutomaticTagConflictResolver> resolvers) { 205 for (String key: tc.getKeysWithMultipleValues()) { 206 for (AutomaticTagConflictResolver resolver : resolvers) { 207 try { 208 if (resolver.matchesKey(key)) { 209 String result = resolver.resolve(tc.getValues(key)); 210 if (result != null) { 211 tc.setUniqueForKey(key, result); 212 break; 213 } 214 } 215 } catch (PatternSyntaxException e) { 216 // Can happen if a particular resolver has an invalid regular expression pattern 217 // but it should not stop the other automatic tag conflict resolution. 218 Main.error(e); 219 } 220 } 221 } 222 } 223 224 /** 225 * Preference for automatic tag-conflict resolver by combining the tag values using a separator. 226 * @since 11606 227 */ 228 public static class AutomaticCombine implements AutomaticTagConflictResolver { 229 230 /** The Tag key to match */ 231 @pref public String key; 232 233 /** A free description */ 234 @pref public String description = ""; 235 236 /** If regular expression must be used to match the Tag key or the value. */ 237 @pref public boolean isRegex; 238 239 /** The separator to use to combine the values. */ 240 @pref public String separator = ";"; 241 242 /** If the combined values must be sorted. 243 * Possible values: 244 * <ul> 245 * <li> Integer - Sort using Integer natural order.</li> 246 * <li> String - Sort using String natural order.</li> 247 * <li> * - No ordering.</li> 248 * </ul> 249 */ 250 @pref public String sort; 251 252 /** Default constructor. */ 253 public AutomaticCombine() { 254 // needed for instantiation from Preferences 255 } 256 257 /** Instantiate an automatic tag-conflict resolver which combining the values using a separator. 258 * @param key The Tag key to match. 259 * @param description A free description. 260 * @param isRegex If regular expression must be used to match the Tag key or the value. 261 * @param separator The separator to use to combine the values. 262 * @param sort If the combined values must be sorted. 263 */ 264 public AutomaticCombine(String key, String description, boolean isRegex, String separator, String sort) { 265 this.key = key; 266 this.description = description; 267 this.isRegex = isRegex; 268 this.separator = separator; 269 this.sort = sort; 270 } 271 272 @Override 273 public boolean matchesKey(String k) { 274 if (isRegex) { 275 return Pattern.matches(this.key, k); 276 } else { 277 return this.key.equals(k); 278 } 279 } 280 281 Set<String> instantiateSortedSet() { 282 if ("String".equals(sort)) { 283 return new TreeSet<>(); 284 } else if ("Integer".equals(sort)) { 285 return new TreeSet<>((String v1, String v2) -> Long.valueOf(v1).compareTo(Long.valueOf(v2))); 286 } else { 287 return new LinkedHashSet<>(); 288 } 289 } 290 291 @Override 292 public String resolve(Set<String> values) { 293 Set<String> results = instantiateSortedSet(); 294 for (String value: values) { 295 for (String part: value.split(Pattern.quote(separator))) { 296 results.add(part); 297 } 298 } 299 return String.join(separator, results); 300 } 301 302 @Override 303 public String toString() { 304 return AutomaticCombine.class.getSimpleName() 305 + "(key='" + key + "', description='" + description + "', isRegex=" 306 + isRegex + ", separator='" + separator + "', sort='" + sort + "')"; 307 } 308 } 309 310 /** 311 * Preference for a particular choice from a group for automatic tag conflict resolution. 312 * {@code AutomaticChoice}s are grouped into {@link AutomaticChoiceGroup}. 313 * @since 11606 314 */ 315 public static class AutomaticChoice { 316 317 /** The Tag key to match. */ 318 @pref public String key; 319 320 /** The name of the {link AutomaticChoice group} this choice belongs to. */ 321 @pref public String group; 322 323 /** A free description. */ 324 @pref public String description = ""; 325 326 /** If regular expression must be used to match the Tag key or the value. */ 327 @pref public boolean isRegex; 328 329 /** The Tag value to match. */ 330 @pref public String value; 331 332 /** 333 * The score to give to this choice in order to choose the best value 334 * Natural String ordering is used to identify the best score. 335 */ 336 @pref public String score; 337 338 /** Default constructor. */ 339 public AutomaticChoice() { 340 // needed for instantiation from Preferences 341 } 342 343 /** 344 * Instantiate a particular choice from a group for automatic tag conflict resolution. 345 * @param key The Tag key to match. 346 * @param group The name of the {link AutomaticChoice group} this choice belongs to. 347 * @param description A free description. 348 * @param isRegex If regular expression must be used to match the Tag key or the value. 349 * @param value The Tag value to match. 350 * @param score The score to give to this choice in order to choose the best value. 351 */ 352 public AutomaticChoice(String key, String group, String description, boolean isRegex, String value, String score) { 353 this.key = key; 354 this.group = group; 355 this.description = description; 356 this.isRegex = isRegex; 357 this.value = value; 358 this.score = score; 359 } 360 361 /** 362 * Check if this choice match the given Tag value. 363 * @param v the Tag value to match. 364 * @return true if this choice correspond to the given tag value. 365 */ 366 public boolean matchesValue(String v) { 367 if (isRegex) { 368 return Pattern.matches(this.value, v); 369 } else { 370 return this.value.equals(v); 371 } 372 } 373 374 /** 375 * Return the score associated to this choice for the given Tag value. 376 * For the result to be valid the given tag value must {@link #matchesValue(String) match} this choice. 377 * @param v the Tag value of which to get the score. 378 * @return the score associated to the given Tag value. 379 * @throws PatternSyntaxException if the regular expression syntax is invalid 380 */ 381 public String computeScoreFromValue(String v) { 382 if (isRegex) { 383 return v.replaceAll("^" + this.value + "$", this.score); 384 } else { 385 return this.score; 386 } 387 } 388 389 @Override 390 public String toString() { 391 return AutomaticChoice.class.getSimpleName() 392 + "(key='" + key + "', group='" + group + "', description='" + description 393 + "', isRegex=" + isRegex + ", value='" + value + "', score='" + score + "')"; 394 } 395 } 396 397 /** 398 * Preference for an automatic tag conflict resolver which choose from 399 * a group of possible {@link AutomaticChoice choice} values. 400 * @since 11606 401 */ 402 public static class AutomaticChoiceGroup implements AutomaticTagConflictResolver { 403 404 /** The Tag key to match. */ 405 @pref public String key; 406 407 /** The name of the group. */ 408 final String group; 409 410 /** If regular expression must be used to match the Tag key. */ 411 @pref public boolean isRegex; 412 413 /** The list of choice to choose from. */ 414 final List<AutomaticChoice> choices; 415 416 /** Instantiate an automatic tag conflict resolver which choose from 417 * a given list of {@link AutomaticChoice choice} values. 418 * 419 * @param key The Tag key to match. 420 * @param group The name of the group. 421 * @param isRegex If regular expression must be used to match the Tag key. 422 * @param choices The list of choice to choose from. 423 */ 424 public AutomaticChoiceGroup(String key, String group, boolean isRegex, List<AutomaticChoice> choices) { 425 this.key = key; 426 this.group = group; 427 this.isRegex = isRegex; 428 this.choices = choices; 429 } 430 431 /** 432 * Group a given list of {@link AutomaticChoice} by the Tag key and the choice group name. 433 * @param choices the list of {@link AutomaticChoice choices} to group. 434 * @return the resulting list of group. 435 */ 436 public static Collection<AutomaticChoiceGroup> groupChoices(Collection<AutomaticChoice> choices) { 437 HashMap<Pair<String, String>, AutomaticChoiceGroup> results = new HashMap<>(); 438 for (AutomaticChoice choice: choices) { 439 Pair<String, String> id = new Pair<>(choice.key, choice.group); 440 AutomaticChoiceGroup group = results.get(id); 441 if (group == null) { 442 boolean isRegex = choice.isRegex && !Pattern.quote(choice.key).equals(choice.key); 443 group = new AutomaticChoiceGroup(choice.key, choice.group, isRegex, new ArrayList<>()); 444 results.put(id, group); 445 } 446 group.choices.add(choice); 447 } 448 return results.values(); 449 } 450 451 @Override 452 public boolean matchesKey(String k) { 453 if (isRegex) { 454 return Pattern.matches(this.key, k); 455 } else { 456 return this.key.equals(k); 457 } 458 } 459 460 @Override 461 public String resolve(Set<String> values) { 462 String bestScore = ""; 463 String bestValue = ""; 464 for (String value : values) { 465 String score = null; 466 for (AutomaticChoice choice : choices) { 467 if (choice.matchesValue(value)) { 468 score = choice.computeScoreFromValue(value); 469 } 470 } 471 if (score == null) { 472 // This value is not matched in this group 473 // so we can not choose from this group for this key. 474 return null; 475 } 476 if (score.compareTo(bestScore) >= 0) { 477 bestScore = score; 478 bestValue = value; 479 } 480 } 481 return bestValue; 482 } 483 484 @Override 485 public String toString() { 486 Collection<String> stringChoices = choices.stream().map(AutomaticChoice::toString).collect(Collectors.toCollection(ArrayList::new)); 487 return AutomaticChoiceGroup.class.getSimpleName() + "(key='" + key + "', group='" + group + 488 "', isRegex=" + isRegex + ", choices=(\n " + String.join(",\n ", stringChoices) + "))"; 489 } 490 } 91 491 }
Note:
See TracChangeset
for help on using the changeset viewer.