Index: src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java	(révision 11568)
+++ src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java	(copie de travail)
@@ -475,7 +475,7 @@
         CheckParameterUtil.ensureParameterNotNull(targetPrimitives, "targetPrimitives");
 
         final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives);
-        TagConflictResolutionUtil.combineTigerTags(completeWayTags);
+        TagConflictResolutionUtil.applyAutomaticTagConflictResolution(completeWayTags);
         TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives);
         final TagCollection tagsToEdit = new TagCollection(completeWayTags);
         TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
Index: src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolutionUtil.java
===================================================================
--- src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolutionUtil.java	(révision 11568)
+++ src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolutionUtil.java	(copie de travail)
@@ -2,12 +2,22 @@
 package org.openstreetmap.josm.gui.conflict.tags;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences.pref;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.osm.TagCollection;
-import org.openstreetmap.josm.data.osm.TigerUtils;
+import org.openstreetmap.josm.tools.Pair;
 
 /**
  * Collection of utility methods for tag conflict resolution
@@ -59,18 +69,6 @@
         }
     }
 
-    /**
-     * Combines tags from TIGER data
-     *
-     * @param tc the tag collection
-     */
-    public static void combineTigerTags(TagCollection tc) {
-        for (String key: tc.getKeys()) {
-            if (TigerUtils.isTigerTag(key)) {
-                tc.setUniqueForKey(key, TigerUtils.combineTags(tc.getValues(key)));
-            }
-        }
-    }
 
     /**
      * Completes tags in the tag collection <code>tc</code> with the empty value
@@ -88,4 +86,306 @@
             tc.add(new Tag(key, ""));
         }
     }
+
+
+    /**
+     * Automatically resolve some configured tag conflicts.
+     * @param tc the tag collection
+     */
+    public static void applyAutomaticTagConflictResolution(TagCollection tc) {
+        applyAutomaticTagConflictResolution(tc, getAutomaticTagConflictCombines(), getAutomaticTagConflictChoiceGroups());
+    }
+
+    public static Collection<AutomaticCombine> defaultAutomaticTagConflictCombines = Arrays.asList(
+            new AutomaticCombine("tiger:tlid", "US TIGER tlid", false, ":", "Integer"),
+            new AutomaticCombine("tiger:(?!tlid$).*", "US TIGER not tlid", true, ":", "String")
+    );
+
+    public static Collection<AutomaticChoice> defaultAutomaticTagConflictChoices = Arrays.asList(
+            // Choice for "source" tag of data exported from the French cadastre,
+            // which ends by the exported year generating many conflicts
+            // the generated score begins with the year number to select the most recent one.
+            new AutomaticChoice("source", "FR cadastre source, manual value", true, "FR:cadastre:source",
+                    "cadastre", "0"),
+            new AutomaticChoice("source", "FR cadastre source, initial format", true, "FR:cadastre:source",
+                    "extraction vectorielle v1 cadastre-dgi-fr source : Direction G[eé]n[eé]rale des Imp[ôo]ts - Cadas\\. Mise [àa] jour : ([0-9]{4})",
+                    "$1 1"),
+            new AutomaticChoice("source", "FR cadastre source, old format", true, "FR:cadastre:source",
+                    "cadastre-dgi-fr source : Direction G[eé]n[eé]rale des Imp[ôo]ts - Cadastre\\. Mise [àa] jour : ([0-9]{4})",
+                    "$1 2"),
+            new AutomaticChoice("source", "FR cadastre source, last format", true, "FR:cadastre:source",
+                    "cadastre-dgi-fr source : Direction G[eé]n[ée]rale des Finances Publiques - Cadastre\\. Mise [aà] jour : ([0-9]{4})",
+                    "$1 3")
+    );
+
+
+    private static volatile Collection<AutomaticChoiceGroup> automaticTagConflictChoiceGroups;
+    public static Collection<AutomaticChoiceGroup> getAutomaticTagConflictChoiceGroups() {
+        if (automaticTagConflictChoiceGroups == null) {
+            automaticTagConflictChoiceGroups = AutomaticChoiceGroup.groupChoices(Main.pref.getListOfStructs(
+                            "automatic-tag-conflict-resolution.choice",
+                            defaultAutomaticTagConflictChoices, AutomaticChoice.class));
+        }
+        return automaticTagConflictChoiceGroups;
+    }
+
+
+    private static volatile Collection<AutomaticCombine> automaticTagConflictCombines;
+    public static Collection<AutomaticCombine> getAutomaticTagConflictCombines() {
+        if (automaticTagConflictCombines == null) {
+            automaticTagConflictCombines = Main.pref.getListOfStructs(
+                            "automatic-tag-conflict-resolution.combine",
+                            defaultAutomaticTagConflictCombines, AutomaticCombine.class);
+        }
+        return automaticTagConflictCombines;
+    }
+
+
+    /**
+     * Automatically resolve some given conflicts.
+     * @param tc the tag collection.
+     * @param combines automatic tag combination to apply.
+     * @param choiceGroups automatic tag choice to apply.
+     */
+    public static void applyAutomaticTagConflictResolution(TagCollection tc, Collection<AutomaticCombine> combines, Collection<AutomaticChoiceGroup> choiceGroups) {
+        keyLoop:
+        for(String key: tc.getKeysWithMultipleValues()) {
+            for(AutomaticCombine combine: combines) {
+                if (combine.matchesKey(key)) {
+                    tc.setUniqueForKey(key, combine.resolve(tc.getValues(key)));
+                    continue keyLoop;
+                }
+            }
+            for(AutomaticChoiceGroup choiceGroup: choiceGroups) {
+                if (choiceGroup.matchesKey(key)) {
+                    String result = choiceGroup.resolve(tc.getValues(key));
+                    if (result != null) {
+                        tc.setUniqueForKey(key, result);
+                        continue keyLoop;
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Preference for automatic tag-conflict resolution by combining the values
+     * using a separator.
+     */
+    public static class AutomaticCombine {
+
+        /** The Tag key to match */
+        @pref public String key;
+
+        /** A free description */
+        @pref public String description;
+
+        /** If regular expression must be used to match the Tag key or the value */
+        @pref public boolean isRegex = false;
+
+        /** The separator to use to combine the values */
+        @pref public String separator = ";";
+
+        /** If the combined values must be sorted, possible values are
+         *  <li>
+         *  <ul>String - using String natural ordering
+         *  <ul>Integer- using Integer natural ordering
+         *  <ul> - whatever else value will keep the original order
+         *  </li>
+         */
+        @pref public String sort;
+
+        public AutomaticCombine() {}
+
+        public AutomaticCombine(String key, String description, boolean isRegex, String separator, String sort) {
+            this.key =  key;
+            this.description = description;
+            this.isRegex = isRegex;
+            this.separator = separator;
+            this.sort = sort;
+        }
+
+        /** Test if this case apply to the given Tag key */
+        public boolean matchesKey(String k) {
+            if (isRegex) {
+                return Pattern.matches(this.key, k);
+            } else {
+                return this.key.equals(k);
+            }
+        }
+
+        Set<String> instantiateSortedSet() {
+            if ("String".equals(sort)) {
+                return new TreeSet<String>();
+            } else if ("Integer".equals(sort)) {
+                return new TreeSet<String>(
+                        (String v1, String v2) -> Long.valueOf(v1).compareTo(Long.valueOf(v2)));
+            } else {
+                return new LinkedHashSet<String>();
+            }
+        }
+
+        public String resolve(Set<String> values) {
+            Set<String> results = instantiateSortedSet();
+            for (String value: values) {
+                for(String part: value.split(separator)) {
+                    results.add(part);
+                }
+            }
+            return String.join(separator, results);
+        }
+
+        @Override
+        public String toString() {
+            return AutomaticCombine.class.getSimpleName()
+                    + "(key='" + key + "', description='" + description + "', isRegex="
+                    + isRegex + ", separator='" + separator + "', sort='" + sort + "')";
+        }
+    }
+
+    /**
+     * Preference for automatic tag conflict resolution by choosing from
+     * a group of possible values.
+     * This class represent a particular choice from a group.
+     */
+    public static class AutomaticChoice {
+
+        /** The Tag key to match */
+        @pref public String key;
+        /** A free description */
+        @pref public String description;
+
+        /** If regular expression must be used to match the Tag key or the value */
+        @pref public boolean isRegex = false;
+
+        /** The name of the group */
+        @pref public String group;
+
+        /** The Tag value to match */
+        @pref public String value;
+
+        /** The score to give to this choice in order to choose the best value */
+        @pref public String score;
+
+        public AutomaticChoice() {}
+
+        public AutomaticChoice(String key, String description, boolean isRegex, String group, String value, String score) {
+            this.key =  key;
+            this.description = description;
+            this.isRegex = isRegex;
+            this.group = group;
+            this.value = value;
+            this.score = score;
+        }
+
+        public boolean matchValue(String v) {
+            if (isRegex) {
+                return Pattern.matches(this.value, v);
+            } else {
+                return this.value.equals(v);
+            }
+        }
+
+        public String computeScoreFromValue(String v) {
+            if (isRegex) {
+                return v.replaceAll("^" + this.value + "$", this.score);
+            } else {
+                return this.score;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return AutomaticChoice.class.getSimpleName()
+                    + "(key='" + key + "', description='" + description + "', isRegex="
+                    + isRegex + ", group='" + group + "', value='" + value + "', score='" + score + "')";
+        }
+    }
+
+
+    /**
+     * Preference for automatic tag conflict resolution by choosing from
+     * a group of possible values.
+     */
+    public static class AutomaticChoiceGroup {
+
+        /** The Tag key to match */
+        @pref public String key;
+
+        /** If regular expression must be used to match the Tag key */
+        @pref public boolean isRegex = false;
+
+        /** The name of the group */
+        public String group;
+
+        /** The list of choice to choose from */
+        public List<AutomaticChoice> choices;
+
+        public AutomaticChoiceGroup(String key, boolean isRegex, String group, List<AutomaticChoice> choices) {
+            this.key =  key;
+            this.isRegex = isRegex;
+            this.group = group;
+            this.choices = choices;
+        }
+
+        public static Collection<AutomaticChoiceGroup> groupChoices(Collection<AutomaticChoice> choices) {
+            HashMap<Pair<String,String>, AutomaticChoiceGroup> results = new HashMap<Pair<String, String>, AutomaticChoiceGroup>();
+            for(AutomaticChoice choice: choices) {
+                Pair<String,String> id = new Pair<String, String>(choice.key, choice.group);
+                AutomaticChoiceGroup group = results.get(id);
+                if (group == null) {
+                    boolean isRegex = choice.isRegex && ! Pattern.quote(choice.key).equals(choice.key);
+                    group = new AutomaticChoiceGroup(choice.key, isRegex, choice.group, new ArrayList<>());
+                    results.put(id, group);
+                }
+                group.choices.add(choice);
+            }
+            return results.values();
+        }
+
+        /** Test if this case apply to the given Tag key */
+        public boolean matchesKey(String k) {
+            if (isRegex) {
+                return Pattern.matches(this.key, k);
+            } else {
+                return this.key.equals(k);
+            }
+        }
+        /**
+         * Choose the most appropriate value for this group, or return null if not possible.
+         */
+        public String resolve(Set<String> values) {
+            String bestScore = "";
+            String bestValue = "";
+            for (String value: values) {
+                String score = null;
+                for(AutomaticChoice choice: choices) {
+                    if (choice.matchValue(value)) {
+                        score = choice.computeScoreFromValue(value);
+                    }
+                }
+                if (score == null) {
+                    // This value is not matched in this group
+                    // so we can not choose from this group for this key.
+                    return null;
+                }
+                if (score.compareTo(bestScore) >= 0) {
+                    bestScore = score;
+                    bestValue = value;
+                }
+            }
+            return bestValue;
+        }
+
+        @Override
+        public String toString() {
+            Collection<String> stringChoices = choices.stream().map(AutomaticChoice::toString).collect(Collectors.toCollection(ArrayList::new));
+            return AutomaticChoiceGroup.class.getSimpleName()
+                    + "(key='" + key + "', isRegex="
+                    + isRegex + ", group='" + group + "', choices=(\n  " + String.join("\n  ", stringChoices) + "))";
+        }
+
+    }
+
 }
