Index: trunk/src/org/openstreetmap/josm/data/preferences/sources/ExtendedSourceEntry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/preferences/sources/ExtendedSourceEntry.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/preferences/sources/ExtendedSourceEntry.java	(revision 12825)
@@ -28,9 +28,11 @@
     /**
      * Constructs a new {@code ExtendedSourceEntry}.
+     * @param type type of source entry
      * @param simpleFileName file name used for display
      * @param url URL that {@link org.openstreetmap.josm.io.CachedFile} understands
+     * @since 12825
      */
-    public ExtendedSourceEntry(String simpleFileName, String url) {
-        super(url, null, null, true);
+    public ExtendedSourceEntry(SourceType type, String simpleFileName, String url) {
+        super(type, url, null, null, true);
         this.simpleFileName = simpleFileName;
     }
Index: trunk/src/org/openstreetmap/josm/data/preferences/sources/MapPaintPrefHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/preferences/sources/MapPaintPrefHelper.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/preferences/sources/MapPaintPrefHelper.java	(revision 12825)
@@ -30,5 +30,5 @@
      */
     public MapPaintPrefHelper() {
-        super("mappaint.style.entries");
+        super("mappaint.style.entries", SourceType.MAP_PAINT_STYLE);
     }
 
@@ -80,10 +80,10 @@
     @Override
     public Collection<ExtendedSourceEntry> getDefault() {
-        ExtendedSourceEntry defJosmMapcss = new ExtendedSourceEntry("elemstyles.mapcss", "resource://styles/standard/elemstyles.mapcss");
+        ExtendedSourceEntry defJosmMapcss = new ExtendedSourceEntry(type, "elemstyles.mapcss", "resource://styles/standard/elemstyles.mapcss");
         defJosmMapcss.active = true;
         defJosmMapcss.name = "standard";
         defJosmMapcss.title = tr("JOSM default (MapCSS)");
         defJosmMapcss.description = tr("Internal style to be used as base for runtime switchable overlay styles");
-        ExtendedSourceEntry defPL2 = new ExtendedSourceEntry("potlatch2.mapcss", "resource://styles/standard/potlatch2.mapcss");
+        ExtendedSourceEntry defPL2 = new ExtendedSourceEntry(type, "potlatch2.mapcss", "resource://styles/standard/potlatch2.mapcss");
         defPL2.active = false;
         defPL2.name = "standard";
@@ -108,5 +108,5 @@
     @Override
     public SourceEntry deserialize(Map<String, String> s) {
-        return new SourceEntry(s.get("url"), s.get("ptoken"), s.get("title"), Boolean.parseBoolean(s.get("active")));
+        return new SourceEntry(type, s.get("url"), s.get("ptoken"), s.get("title"), Boolean.parseBoolean(s.get("active")));
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/preferences/sources/PresetPrefHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/preferences/sources/PresetPrefHelper.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/preferences/sources/PresetPrefHelper.java	(revision 12825)
@@ -24,10 +24,10 @@
      */
     public PresetPrefHelper() {
-        super("taggingpreset.entries");
+        super("taggingpreset.entries", SourceType.TAGGING_PRESET);
     }
 
     @Override
     public Collection<ExtendedSourceEntry> getDefault() {
-        ExtendedSourceEntry i = new ExtendedSourceEntry("defaultpresets.xml", "resource://data/defaultpresets.xml");
+        ExtendedSourceEntry i = new ExtendedSourceEntry(type, "defaultpresets.xml", "resource://data/defaultpresets.xml");
         i.title = tr("Internal Preset");
         i.description = tr("The default preset for JOSM");
@@ -45,5 +45,5 @@
     @Override
     public SourceEntry deserialize(Map<String, String> s) {
-        return new SourceEntry(s.get("url"), null, s.get("title"), true);
+        return new SourceEntry(type, s.get("url"), null, s.get("title"), true);
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/preferences/sources/SourceEntry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/preferences/sources/SourceEntry.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/preferences/sources/SourceEntry.java	(revision 12825)
@@ -19,4 +19,10 @@
 
     /**
+     * The type of source entry.
+     * @since 12825
+     **/
+    public final SourceType type;
+
+    /**
      *  A URL can be anything that CachedFile understands, i.e.
      *  a local file, http://, or a file from the current jar
@@ -58,4 +64,5 @@
     /**
      * Constructs a new {@code SourceEntry}.
+     * @param type type of source entry
      * @param url URL that {@link org.openstreetmap.josm.io.CachedFile} understands
      * @param isZip if url is a zip file and the resource is inside the zip file
@@ -70,6 +77,8 @@
      * @see #title
      * @see #active
-     */
-    public SourceEntry(String url, boolean isZip, String zipEntryPath, String name, String title, boolean active) {
+     * @since 12825
+     */
+    public SourceEntry(SourceType type, String url, boolean isZip, String zipEntryPath, String name, String title, boolean active) {
+        this.type = type;
         this.url = url;
         this.isZip = isZip;
@@ -82,4 +91,5 @@
     /**
      * Constructs a new {@code SourceEntry}.
+     * @param type type of source entry
      * @param url URL that {@link org.openstreetmap.josm.io.CachedFile} understands
      * @param name Source name
@@ -90,7 +100,8 @@
      * @see #title
      * @see #active
-     */
-    public SourceEntry(String url, String name, String title, boolean active) {
-        this(url, false, null, name, title, active);
+     * @since 12825
+     */
+    public SourceEntry(SourceType type, String url, String name, String title, boolean active) {
+        this(type, url, false, null, name, title, active);
     }
 
@@ -100,4 +111,5 @@
      */
     public SourceEntry(SourceEntry e) {
+        this.type = e.type;
         this.url = e.url;
         this.isZip = e.isZip;
Index: trunk/src/org/openstreetmap/josm/data/preferences/sources/SourcePrefHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/preferences/sources/SourcePrefHelper.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/preferences/sources/SourcePrefHelper.java	(revision 12825)
@@ -19,11 +19,15 @@
 
     private final String pref;
+    protected final SourceType type;
 
     /**
      * Constructs a new {@code SourcePrefHelper} for the given preference key.
      * @param pref The preference key
+     * @param type The source type
+     * @since 12825
      */
-    public SourcePrefHelper(String pref) {
+    public SourcePrefHelper(String pref, SourceType type) {
         this.pref = pref;
+        this.type = type;
     }
 
Index: trunk/src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java	(revision 12825)
@@ -55,5 +55,5 @@
      */
     public ValidatorPrefHelper() {
-        super(MapCSSTagChecker.ENTRIES_PREF_KEY);
+        super(MapCSSTagChecker.ENTRIES_PREF_KEY, SourceType.TAGCHECKER_RULE);
     }
 
@@ -80,6 +80,6 @@
     }
 
-    private static void addDefault(List<ExtendedSourceEntry> defaults, String filename, String title, String description) {
-        ExtendedSourceEntry i = new ExtendedSourceEntry(filename+".mapcss", "resource://data/validator/"+filename+".mapcss");
+    private void addDefault(List<ExtendedSourceEntry> defaults, String filename, String title, String description) {
+        ExtendedSourceEntry i = new ExtendedSourceEntry(type, filename+".mapcss", "resource://data/validator/"+filename+".mapcss");
         i.title = title;
         i.description = description;
@@ -98,5 +98,5 @@
     @Override
     public SourceEntry deserialize(Map<String, String> s) {
-        return new SourceEntry(s.get("url"), null, s.get("title"), Boolean.parseBoolean(s.get("active")));
+        return new SourceEntry(type, s.get("url"), null, s.get("title"), Boolean.parseBoolean(s.get("active")));
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 12825)
@@ -2,5 +2,4 @@
 package org.openstreetmap.josm.data.validation.tests;
 
-import static org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.FixCommand.evaluateObject;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -42,4 +41,5 @@
 import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
+import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.Test;
@@ -760,5 +760,5 @@
                 addMapCSS(i);
                 if (Main.pref.getBoolean("validator.auto_reload_local_rules", true) && source.isLocal()) {
-                    Main.fileWatcher.registerValidatorRule(source);
+                    Main.fileWatcher.registerSource(source);
                 }
             } catch (IOException | IllegalStateException | IllegalArgumentException ex) {
@@ -823,3 +823,19 @@
         return Objects.equals(checks, that.checks);
     }
+
+    /**
+     * Reload tagchecker rule.
+     * @param rule tagchecker rule to reload
+     * @since 12825
+     */
+    public static void reloadRule(SourceEntry rule) {
+        MapCSSTagChecker tagChecker = OsmValidator.getTest(MapCSSTagChecker.class);
+        if (tagChecker != null) {
+            try {
+                tagChecker.addMapCSS(rule.url);
+            } catch (IOException | ParseException e) {
+                Logging.warn(e);
+            }
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 12825)
@@ -88,4 +88,5 @@
 import org.openstreetmap.josm.data.osm.UserInfo;
 import org.openstreetmap.josm.data.osm.search.SearchMode;
+import org.openstreetmap.josm.data.preferences.sources.SourceType;
 import org.openstreetmap.josm.data.projection.ProjectionCLI;
 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileSource;
@@ -93,4 +94,5 @@
 import org.openstreetmap.josm.data.projection.datum.NTV2Proj4DirGridShiftFileSource;
 import org.openstreetmap.josm.data.validation.OsmValidator;
+import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
 import org.openstreetmap.josm.gui.ProgramArguments.Option;
 import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor;
@@ -110,4 +112,5 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
+import org.openstreetmap.josm.gui.mappaint.loader.MapPaintStyleLoader;
 import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
@@ -126,4 +129,5 @@
 import org.openstreetmap.josm.io.CertificateAmendment;
 import org.openstreetmap.josm.io.DefaultProxySelector;
+import org.openstreetmap.josm.io.FileWatcher;
 import org.openstreetmap.josm.io.MessageNotifier;
 import org.openstreetmap.josm.io.OnlineResource;
@@ -1094,4 +1098,6 @@
         MessageNotifier.setNotifierCallback(MainApplication::notifyNewMessages);
         DeleteCommand.setDeletionCallback(DeleteAction.defaultDeletionCallback);
+        FileWatcher.registerLoader(SourceType.MAP_PAINT_STYLE, MapPaintStyleLoader::reloadStyle);
+        FileWatcher.registerLoader(SourceType.TAGCHECKER_RULE, MapCSSTagChecker::reloadRule);
         OsmUrlToBounds.setMapSizeSupplier(() -> {
             if (isDisplayingMapView()) {
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 12825)
@@ -301,5 +301,5 @@
         if (Main.pref.getBoolean("mappaint.auto_reload_local_styles", true) && source.isLocal()) {
             try {
-                Main.fileWatcher.registerStyleSource(source);
+                Main.fileWatcher.registerSource(source);
             } catch (IOException | IllegalStateException | IllegalArgumentException e) {
                 Logging.error(e);
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSource.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSource.java	(revision 12825)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
+import org.openstreetmap.josm.data.preferences.sources.SourceType;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
 import org.openstreetmap.josm.io.CachedFile;
@@ -70,5 +71,5 @@
      */
     public StyleSource(String url, String name, String title) {
-        super(url, name, title, true);
+        super(SourceType.MAP_PAINT_STYLE, url, name, title, true);
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/loader/MapPaintStyleLoader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/loader/MapPaintStyleLoader.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/loader/MapPaintStyleLoader.java	(revision 12825)
@@ -6,6 +6,8 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
+import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
@@ -67,3 +69,17 @@
         MainApplication.worker.submit(new MapPaintStyleLoader(toReload));
     }
+
+    /**
+     * Reload style.
+     * @param style {@link StyleSource} to reload
+     * @throws IllegalArgumentException if {@code style} is not a {@code StyleSource} instance
+     * @since 12825
+     */
+    public static void reloadStyle(SourceEntry style) {
+        if (style instanceof StyleSource) {
+            MainApplication.worker.submit(new MapPaintStyleLoader(Collections.singleton((StyleSource) style)));
+        } else {
+            throw new IllegalArgumentException(style + " is not a StyleSource");
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 12825)
@@ -741,5 +741,5 @@
             if (sources == null) return;
             for (ExtendedSourceEntry info: sources) {
-                data.add(new SourceEntry(info.url, info.name, info.getDisplayName(), true));
+                data.add(new SourceEntry(info.type, info.url, info.name, info.getDisplayName(), true));
             }
             fireTableDataChanged();
@@ -969,5 +969,5 @@
                     active = editEntryDialog.active();
                 }
-                final SourceEntry entry = new SourceEntry(
+                final SourceEntry entry = new SourceEntry(sourceType,
                         editEntryDialog.getURL(),
                         null, editEntryDialog.getTitle(), active);
@@ -1509,5 +1509,5 @@
                         Matcher m = Pattern.compile("^(.+);(.+)$").matcher(line);
                         if (m.matches()) {
-                            last = new ExtendedSourceEntry(m.group(1), m.group(2));
+                            last = new ExtendedSourceEntry(sourceType, m.group(1), m.group(2));
                             sources.add(last);
                         } else {
Index: trunk/src/org/openstreetmap/josm/io/FileWatcher.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/FileWatcher.java	(revision 12824)
+++ trunk/src/org/openstreetmap/josm/io/FileWatcher.java	(revision 12825)
@@ -12,18 +12,15 @@
 import java.nio.file.WatchKey;
 import java.nio.file.WatchService;
-import java.util.Collections;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.Executors;
+import java.util.Objects;
+import java.util.function.Consumer;
 
 import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
-import org.openstreetmap.josm.data.validation.OsmValidator;
-import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
+import org.openstreetmap.josm.data.preferences.sources.SourceType;
 import org.openstreetmap.josm.gui.mappaint.StyleSource;
-import org.openstreetmap.josm.gui.mappaint.loader.MapPaintStyleLoader;
-import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -36,6 +33,6 @@
     private Thread thread;
 
-    private final Map<Path, StyleSource> styleMap = new HashMap<>();
-    private final Map<Path, SourceEntry> ruleMap = new HashMap<>();
+    private static final Map<SourceType, Consumer<SourceEntry>> loaderMap = new EnumMap<>(SourceType.class);
+    private final Map<Path, SourceEntry> sourceMap = new HashMap<>();
 
     /**
@@ -66,7 +63,9 @@
      * @throws IllegalStateException if the watcher service failed to start
      * @throws IOException if an I/O error occurs
+     * @deprecated To be removed end of 2017. Use {@link #registerSource} instead
      */
+    @Deprecated
     public void registerStyleSource(StyleSource style) throws IOException {
-        register(style, styleMap);
+        registerSource(style);
     }
 
@@ -78,20 +77,30 @@
      * @throws IOException if an I/O error occurs
      * @since 7276
+     * @deprecated To be removed end of 2017. Use {@link #registerSource} instead
      */
+    @Deprecated
     public void registerValidatorRule(SourceEntry rule) throws IOException {
-        register(rule, ruleMap);
+        registerSource(rule);
     }
 
-    private <T extends SourceEntry> void register(T obj, Map<Path, T> map) throws IOException {
-        CheckParameterUtil.ensureParameterNotNull(obj, "obj");
+    /**
+     * Registers a source for local file changes, allowing dynamic reloading.
+     * @param src The source to watch
+     * @throws IllegalArgumentException if {@code rule} is null or if it does not provide a local file
+     * @throws IllegalStateException if the watcher service failed to start
+     * @throws IOException if an I/O error occurs
+     * @since 12825
+     */
+    public void registerSource(SourceEntry src) throws IOException {
+        CheckParameterUtil.ensureParameterNotNull(src, "src");
         if (watcher == null) {
             throw new IllegalStateException("File watcher is not available");
         }
         // Get local file, as this method is only called for local style sources
-        File file = new File(obj.url);
+        File file = new File(src.url);
         // Get parent directory as WatchService allows only to monitor directories, not single files
         File dir = file.getParentFile();
         if (dir == null) {
-            throw new IllegalArgumentException("Resource "+obj+" does not have a parent directory");
+            throw new IllegalArgumentException("Resource "+src+" does not have a parent directory");
         }
         synchronized (this) {
@@ -99,6 +108,17 @@
             // (it returns the same key so it should not send events several times)
             dir.toPath().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE);
-            map.put(file.toPath(), obj);
+            sourceMap.put(file.toPath(), src);
         }
+    }
+
+    /**
+     * Registers a source loader, allowing dynamic reloading when an entry changes.
+     * @param type the source type for which the loader operates
+     * @param loader the loader in charge of reloading any source of given type when it changes
+     * @return the previous loader registered for this source type, if any
+     * @since 12825
+     */
+    public static Consumer<SourceEntry> registerLoader(SourceType type, Consumer<SourceEntry> loader) {
+        return loaderMap.put(Objects.requireNonNull(type, "type"), Objects.requireNonNull(loader, "loader"));
     }
 
@@ -148,19 +168,12 @@
 
                 synchronized (this) {
-                    StyleSource style = styleMap.get(fullPath);
-                    SourceEntry rule = ruleMap.get(fullPath);
-                    if (style != null) {
-                        Logging.info("Map style "+style.getDisplayString()+" has been modified. Reloading style...");
-                        Executors.newSingleThreadExecutor(Utils.newThreadFactory("mapstyle-reload-%d", Thread.NORM_PRIORITY)).submit(
-                                new MapPaintStyleLoader(Collections.singleton(style)));
-                    } else if (rule != null) {
-                        Logging.info("Validator rule "+rule.getDisplayString()+" has been modified. Reloading rule...");
-                        MapCSSTagChecker tagChecker = OsmValidator.getTest(MapCSSTagChecker.class);
-                        if (tagChecker != null) {
-                            try {
-                                tagChecker.addMapCSS(rule.url);
-                            } catch (IOException | ParseException e) {
-                                Logging.warn(e);
-                            }
+                    SourceEntry source = sourceMap.get(fullPath);
+                    if (source != null) {
+                        Consumer<SourceEntry> loader = loaderMap.get(source.type);
+                        if (loader != null) {
+                            Logging.info("Source "+source.getDisplayString()+" has been modified. Reloading it...");
+                            loader.accept(source);
+                        } else {
+                            Logging.warn("Received {0} event for unregistered source type: {1}", kind.name(), source.type);
                         }
                     } else if (Logging.isDebugEnabled()) {
