Index: src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- src/org/openstreetmap/josm/data/Preferences.java	(revision 11730)
+++ src/org/openstreetmap/josm/data/Preferences.java	(working copy)
@@ -70,6 +70,7 @@
 import org.openstreetmap.josm.gui.preferences.SourceEditor.ExtendedSourceEntry;
 import org.openstreetmap.josm.gui.preferences.validator.ValidatorTagCheckerRulesPreference;
 import org.openstreetmap.josm.gui.preferences.validator.ValidatorTagCheckerRulesPreference.RulePrefHelper;
+import org.openstreetmap.josm.io.CachedFile;
 import org.openstreetmap.josm.io.OfflineAccessException;
 import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -592,15 +593,15 @@
      * Called after every put. In case of a problem, do nothing but output the error in log.
      * @throws IOException if any I/O error occurs
      */
-    public synchronized void save() throws IOException {
-        save(getPreferenceFile(), settingsMap.entrySet().stream().filter(NO_DEFAULT_SETTINGS_ENTRY), false);
+    public void save() throws IOException {
+        save(getPreferenceFile(), settingsMap.entrySet().stream().filter(NO_DEFAULT_SETTINGS_ENTRY), false, false);
     }
 
-    public synchronized void saveDefaults() throws IOException {
-        save(getDefaultsCacheFile(), defaultsMap.entrySet().stream(), true);
+    public void saveDefaults() throws IOException {
+        save(getDefaultsCacheFile(), defaultsMap.entrySet().stream(), true, false);
     }
 
-    protected void save(File prefFile, Stream<Entry<String, Setting<?>>> settings, boolean defaults) throws IOException {
+    public synchronized void save(File prefFile, Stream<Entry<String, Setting<?>>> settings, boolean defaults, boolean addTime) throws IOException {
         if (!defaults) {
             /* currently unused, but may help to fix configuration issues in future */
             putInteger("josm.version", Version.getInstance().getVersion());
@@ -616,7 +617,7 @@
         }
 
         try (PreferencesWriter writer = new PreferencesWriter(
-                new PrintWriter(new File(prefFile + "_tmp"), StandardCharsets.UTF_8.name()), false, defaults)) {
+                new PrintWriter(new File(prefFile + "_tmp"), StandardCharsets.UTF_8.name()), false, defaults, addTime)) {
             writer.write(settings);
         }
 
@@ -655,7 +656,7 @@
     protected void load() throws IOException, SAXException, XMLStreamException {
         File pref = getPreferenceFile();
         PreferencesReader.validateXML(pref);
-        PreferencesReader reader = new PreferencesReader(pref, false);
+        PreferencesReader reader = new PreferencesReader(pref, false, false);
         reader.parse();
         settingsMap.clear();
         settingsMap.putAll(reader.getSettings());
@@ -675,9 +676,8 @@
     protected void loadDefaults() throws IOException, XMLStreamException, SAXException {
         File def = getDefaultsCacheFile();
         PreferencesReader.validateXML(def);
-        PreferencesReader reader = new PreferencesReader(def, true);
+        PreferencesReader reader = new PreferencesReader(def, true, true);
         reader.parse();
-        defaultsMap.clear();
         long minTime = System.currentTimeMillis() / 1000 - MAX_AGE_DEFAULT_PREFERENCES;
         for (Entry<String, Setting<?>> e : reader.getSettings().entrySet()) {
             if (e.getValue().getTime() >= minTime) {
@@ -686,6 +686,15 @@
         }
     }
 
+    protected void loadAvailable() throws XMLStreamException, IOException {
+        try (CachedFile cf = new CachedFile("resource://data/preferences-available.xml");
+                Reader rd = cf.getContentReader()) {
+            PreferencesReader reader = new PreferencesReader(rd, true, false);
+            reader.parse();
+            defaultsMap.putAll(reader.getSettings());
+        }
+    }
+
     /**
      * Loads preferences from XML reader.
      * @param in XML reader
@@ -693,7 +702,7 @@
      * @throws IOException if any I/O error occurs
      */
     public void fromXML(Reader in) throws XMLStreamException, IOException {
-        PreferencesReader reader = new PreferencesReader(in, false);
+        PreferencesReader reader = new PreferencesReader(in, false, false);
         reader.parse();
         settingsMap.clear();
         settingsMap.putAll(reader.getSettings());
@@ -782,6 +791,13 @@
                 Main.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile()));
             }
         }
+        try {
+            loadAvailable();
+        } catch (XMLStreamException | IOException e) {
+            Main.error(e);
+            Main.warn(tr("Failed to load available preferences."));
+            defaultsMap.clear();
+        }
         File def = getDefaultsCacheFile();
         if (def.exists()) {
             try {
@@ -1473,7 +1489,7 @@
      * @return XML
      */
     public String toXML(boolean nopass) {
-        return toXML(settingsMap.entrySet(), nopass, false);
+        return toXML(settingsMap.entrySet(), nopass, false, false);
     }
 
     /**
@@ -1484,10 +1500,10 @@
      * regular preferences
      * @return XML
      */
-    public String toXML(Collection<Entry<String, Setting<?>>> settings, boolean nopass, boolean defaults) {
+    public String toXML(Collection<Entry<String, Setting<?>>> settings, boolean nopass, boolean defaults, boolean addTime) {
         try (
             StringWriter sw = new StringWriter();
-            PreferencesWriter prefWriter = new PreferencesWriter(new PrintWriter(sw), nopass, defaults)
+            PreferencesWriter prefWriter = new PreferencesWriter(new PrintWriter(sw), nopass, defaults, addTime)
         ) {
             prefWriter.write(settings);
             sw.flush();
Index: src/org/openstreetmap/josm/data/preferences/PreferencesReader.java
===================================================================
--- src/org/openstreetmap/josm/data/preferences/PreferencesReader.java	(revision 11730)
+++ src/org/openstreetmap/josm/data/preferences/PreferencesReader.java	(working copy)
@@ -46,6 +46,7 @@
     private final File file;
 
     private final boolean defaults;
+    private final boolean expectTime;
 
     /**
      * Constructs a new {@code PreferencesReader}.
@@ -53,10 +54,11 @@
      * @param defaults true when reading from the cache file for default preferences,
      * false for the regular preferences config file
      */
-    public PreferencesReader(File file, boolean defaults) {
+    public PreferencesReader(File file, boolean defaults, boolean expectTime) {
         this.defaults = defaults;
         this.reader = null;
         this.file = file;
+        this.expectTime = expectTime;
     }
 
     /**
@@ -65,10 +67,11 @@
      * @param defaults true when reading from the cache file for default preferences,
      * false for the regular preferences config file
      */
-    public PreferencesReader(Reader reader, boolean defaults) {
+    public PreferencesReader(Reader reader, boolean defaults, boolean expectTime) {
         this.defaults = defaults;
         this.reader = reader;
         this.file = null;
+        this.expectTime = expectTime;
     }
 
     /**
@@ -176,7 +179,7 @@
                         setting = new StringSetting(Optional.ofNullable(parser.getAttributeValue(null, "value"))
                                 .orElseThrow(() -> new XMLStreamException(tr("value expected"), parser.getLocation())));
                     }
-                    if (defaults) {
+                    if (expectTime) {
                         setting.setTime(Math.round(Double.parseDouble(parser.getAttributeValue(null, "time"))));
                     }
                     settings.put(parser.getAttributeValue(null, "key"), setting);
@@ -210,7 +213,7 @@
     private void parseToplevelList() throws XMLStreamException {
         String key = parser.getAttributeValue(null, "key");
         Long time = null;
-        if (defaults) {
+        if (expectTime) {
             time = Math.round(Double.parseDouble(parser.getAttributeValue(null, "time")));
         }
         String name = parser.getLocalName();
@@ -231,8 +234,10 @@
                     setting = new ListSetting(null);
                     break;
             }
-            setting.setTime(time);
-            settings.put(key, setting);
+            if (expectTime) {
+                setting.setTime(time);
+                settings.put(key, setting);
+            }
             jumpToEnd();
         } else {
             while (true) {
@@ -286,7 +291,7 @@
                         break;
                 }
             }
-            if (defaults) {
+            if (expectTime) {
                 setting.setTime(time);
             }
             settings.put(key, setting);
Index: src/org/openstreetmap/josm/data/preferences/PreferencesWriter.java
===================================================================
--- src/org/openstreetmap/josm/data/preferences/PreferencesWriter.java	(revision 11730)
+++ src/org/openstreetmap/josm/data/preferences/PreferencesWriter.java	(working copy)
@@ -19,6 +19,7 @@
 public class PreferencesWriter extends XmlWriter implements SettingVisitor {
     private final boolean noPassword;
     private final boolean defaults;
+    private final boolean addTime;
     private String key;
 
     /**
@@ -27,10 +28,11 @@
      * @param noPassword if password must be excluded
      * @param defaults true, if default values are converted to XML, false for regular preferences
      */
-    public PreferencesWriter(PrintWriter out, boolean noPassword, boolean defaults) {
+    public PreferencesWriter(PrintWriter out, boolean noPassword, boolean defaults, boolean addTime) {
         super(out);
         this.noPassword = noPassword;
         this.defaults = defaults;
+        this.addTime = addTime;
     }
 
     /**
@@ -67,7 +69,7 @@
     }
 
     private void addTime(Setting<?> setting) {
-        if (defaults) {
+        if (addTime) {
             out.write("' time='" + Optional.ofNullable(setting.getTime()).orElseThrow(IllegalStateException::new));
         }
     }
Index: src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeRestrictionPanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeRestrictionPanel.java	(revision 11730)
+++ src/org/openstreetmap/josm/gui/dialogs/changeset/query/TimeRestrictionPanel.java	(working copy)
@@ -293,7 +293,7 @@
      * Remember settings in preferences.
      */
     public void rememberSettings() {
-        String prefRoot = "changeset-query.advanced.time-restrictions";
+        final String prefRoot = "changeset-query.advanced.time-restrictions";
         if (rbClosedAfter.isSelected()) {
             Main.pref.put(prefRoot + ".query-type", "closed-after");
         } else if (rbClosedAfterAndCreatedBefore.isSelected()) {
@@ -311,7 +311,7 @@
      * Restore settings from preferences.
      */
     public void restoreFromSettings() {
-        String prefRoot = "changeset-query.advanced.open-restrictions";
+        final String prefRoot = "changeset-query.advanced.open-restrictions";
         String v = Main.pref.get(prefRoot + ".query-type", "closed-after");
         rbClosedAfter.setSelected("closed-after".equals(v));
         rbClosedAfterAndCreatedBefore.setSelected("closed-after-created-before".equals(v));
Index: scripts/BuildProjectionDefinitions.java
===================================================================
--- scripts/BuildProjectionDefinitions.java	(revision 11730)
+++ scripts/BuildProjectionDefinitions.java	(working copy)
@@ -45,7 +45,7 @@
 
     /**
      * Program entry point
-     * @param args command line arguments (not used)
+     * @param args command line arguments: 1st argument - base directory
      * @throws IOException if any I/O error occurs
      */
     public static void main(String[] args) throws IOException {
Index: scripts/PreferencesCollector.java
===================================================================
--- scripts/PreferencesCollector.java	(revision 0)
+++ scripts/PreferencesCollector.java	(working copy)
@@ -0,0 +1,329 @@
+// License: GPL. For details, see LICENSE file.
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.preferences.CollectionProperty;
+import org.openstreetmap.josm.data.preferences.ColorProperty;
+import org.openstreetmap.josm.data.preferences.DoubleProperty;
+import org.openstreetmap.josm.data.preferences.EnumProperty;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.data.preferences.ListListSetting;
+import org.openstreetmap.josm.data.preferences.ListSetting;
+import org.openstreetmap.josm.data.preferences.LongProperty;
+import org.openstreetmap.josm.data.preferences.MapListSetting;
+import org.openstreetmap.josm.data.preferences.Setting;
+import org.openstreetmap.josm.data.preferences.StringProperty;
+import org.openstreetmap.josm.data.preferences.StringSetting;
+import org.openstreetmap.josm.data.preferences.StrokeProperty;
+import org.openstreetmap.josm.tools.I18n;
+
+import spoon.Launcher;
+import spoon.processing.AbstractProcessor;
+import spoon.reflect.code.BinaryOperatorKind;
+import spoon.reflect.code.CtAbstractInvocation;
+import spoon.reflect.code.CtBinaryOperator;
+import spoon.reflect.code.CtConstructorCall;
+import spoon.reflect.code.CtExpression;
+import spoon.reflect.code.CtFieldRead;
+import spoon.reflect.code.CtInvocation;
+import spoon.reflect.code.CtLiteral;
+import spoon.reflect.code.CtUnaryOperator;
+import spoon.reflect.code.CtVariableRead;
+import spoon.reflect.code.UnaryOperatorKind;
+import spoon.reflect.declaration.CtClass;
+import spoon.reflect.declaration.CtElement;
+import spoon.reflect.declaration.CtField;
+import spoon.reflect.declaration.CtVariable;
+import spoon.reflect.declaration.ModifierKind;
+import spoon.reflect.eval.PartialEvaluator;
+import spoon.reflect.reference.CtExecutableReference;
+import spoon.reflect.reference.CtFieldReference;
+import spoon.reflect.reference.CtVariableReference;
+import spoon.reflect.visitor.filter.AbstractFilter;
+import spoon.support.SpoonClassNotFoundException;
+
+public class PreferencesCollector extends AbstractProcessor<CtAbstractInvocation> {
+
+    public SortedMap<String, Setting<?>> settingsMap = new TreeMap<>();
+    public int numNull = 0;
+
+    public static void main(String[] args) {
+        String baseDir = args[0];
+        Launcher spoon = new Launcher();
+        spoon.addInputResource(baseDir + "/src/org/openstreetmap/josm");
+        spoon.buildModel();
+        PreferencesCollector coll = new PreferencesCollector();
+        for (CtAbstractInvocation inv : spoon.getFactory().Package().getRootPackage().getElements(new AbstractFilter<CtAbstractInvocation>() {
+            @Override
+            public boolean matches(CtAbstractInvocation element) {
+                return element instanceof CtInvocation || element instanceof CtConstructorCall;
+            }
+        })) {
+            coll.process(inv);
+        }
+        try {
+            System.err.println("number of unique recognized keys:              " + coll.settingsMap.keySet().size());
+            System.err.println("number of unrecognized keys (with duplicates): " + coll.numNull);
+            Main.pref.save(new File(baseDir + "/data/preferences-available.xml"), coll.settingsMap.entrySet().stream(), true, false);
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    @Override
+    public void process(CtAbstractInvocation element) {
+        Map<Executable, Setting<?>> prefExecutables = new HashMap<>();
+        try {
+            // don't include Preferences.getInteger(String key, String specName, int def) as it
+            // is usually called with non-constant parameter specName
+            prefExecutables.put(Preferences.class.getMethod("get", String.class), new StringSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("get", String.class, String.class), new StringSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getCollection", String.class), new ListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getCollection", String.class, Collection.class), new ListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getArray", String.class), new ListListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getArray", String.class, Collection.class), new ListListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getBoolean", String.class), new StringSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getBoolean", String.class, boolean.class), new StringSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getInteger", String.class, int.class), new StringSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getDouble", String.class, double.class), new StringSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getListOfStructs", String.class, Collection.class), new MapListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getListOfStructs", String.class, Class.class), new MapListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getListOfStructs", String.class, Collection.class, Class.class), new MapListSetting(null));
+            prefExecutables.put(Preferences.class.getMethod("getLong", String.class, long.class), new StringSetting(null));
+            prefExecutables.put(StringProperty.class.getConstructor(String.class, String.class), new StringSetting(null));
+            prefExecutables.put(IntegerProperty.class.getConstructor(String.class, int.class), new StringSetting(null));
+            prefExecutables.put(LongProperty.class.getConstructor(String.class, long.class), new StringSetting(null));
+            prefExecutables.put(BooleanProperty.class.getConstructor(String.class, boolean.class), new StringSetting(null));
+            prefExecutables.put(DoubleProperty.class.getConstructor(String.class, double.class), new StringSetting(null));
+            prefExecutables.put(CollectionProperty.class.getConstructor(String.class, Collection.class), new ListSetting(null));
+            prefExecutables.put(StrokeProperty.class.getConstructor(String.class, String.class), new StringSetting(null));
+            prefExecutables.put(EnumProperty.class.getConstructor(String.class, Class.class, Enum.class), new StringSetting(null));
+            prefExecutables.put(ColorProperty.class.getConstructor(String.class, String.class), new StringSetting(null));
+            prefExecutables.put(ColorProperty.class.getConstructor(String.class, Color.class), new StringSetting(null));
+        } catch (NoSuchMethodException | SecurityException ex) {
+            throw new RuntimeException(ex);
+        }
+        CtExecutableReference exec = element.getExecutable();
+        Executable realExec = null;
+        if (element instanceof CtInvocation) {
+            realExec = exec.getActualMethod();
+        } else if (element instanceof CtConstructorCall) {
+            try {
+                realExec = exec.getActualConstructor();
+            } catch (SpoonClassNotFoundException cnf) {
+                // it seems to have trouble with certain nested inner / anonymous classes
+            }
+        } else throw new AssertionError();
+
+        Setting setting = prefExecutables.get(realExec);
+        if (setting != null) {
+            List<CtExpression<?>> args = element.getArguments();
+            CtExpression key = args.get(0);
+            StaticCodeEvaluator eval = new StaticCodeEvaluator();
+            eval.evaluate(key);
+            String strKey = eval.getResult();
+            if (strKey != null) {
+                // fix ColorProperty
+                Class klass = realExec.getDeclaringClass();
+                if (klass.equals(ColorProperty.class)) {
+                    strKey = ColorProperty.getColorKey(strKey);
+                }
+
+                // add default value, if possilbe
+                if (setting instanceof StringSetting && args.size() >= 2) {
+                    CtExpression defaultValue = args.get(args.size() - 1);
+                    StaticCodeEvaluator eval2 = new StaticCodeEvaluator();
+                    eval2.evaluate(defaultValue);
+                    String strDefault = eval2.getResult();
+                    if (strDefault != null) {
+                        setting = new StringSetting(strDefault);
+                    } else {
+                        System.err.println("=== default value extraction failed!");
+                        System.err.println("file: "+element.getPosition().getLine()+":"+element.getPosition().getColumn() +element.getPosition().getFile());
+                        System.err.println("v: "+defaultValue.getClass()+ " "+ defaultValue);
+                        System.err.println(eval.getErrorLog());
+                    }
+                }
+                settingsMap.put(strKey, setting);
+            } else {
+                System.err.println("=== key extraction failed!");
+                System.err.println("file: "+element.getPosition().getLine()+":"+element.getPosition().getColumn() +element.getPosition().getFile());
+                System.err.println("exec: "+element.getExecutable());
+                System.err.println("args: "+element.getArguments());
+                System.err.println(eval.getErrorLog());
+                numNull++;
+            }
+        }
+    }
+
+    public static Class determineClass(CtElement element) {
+        CtClass klass = element.getParent(CtClass.class);
+        if (klass == null)
+            return null;
+        return klass.getActualClass();
+    }
+
+    public static class StaticCodeEvaluator implements PartialEvaluator {
+
+        private String result;
+        private StringBuilder sb = new StringBuilder();
+
+        private final Method marktr;
+
+        public StaticCodeEvaluator() {
+            try {
+                marktr = I18n.class.getMethod("marktr", String.class);
+            } catch (NoSuchMethodException | SecurityException ex) {
+                throw new RuntimeException();
+            }
+        }
+
+        @Override
+        public <R extends CtElement> R evaluate(R element) {
+            Object oResult = evalWrk(element, 0);
+            if (oResult != null) {
+                this.result = oResult.toString();
+            }
+            return null;
+        }
+
+        protected Object evalWrk(CtElement element, int lvl) {
+            lvl++;
+            if (lvl > 10) {
+                log("maximum recursion level exceeded!");
+                return null;
+            }
+            if (element instanceof CtLiteral) {
+                return eval((CtLiteral) element, lvl);
+            } else if (element instanceof CtFieldRead) {
+                return eval((CtFieldRead) element, lvl);
+            } else if (element instanceof CtVariableRead) {
+                return eval((CtVariableRead) element, lvl);
+            } else if (element instanceof CtUnaryOperator) {
+                return eval((CtUnaryOperator) element, lvl);
+            } else if (element instanceof CtBinaryOperator) {
+                return eval((CtBinaryOperator) element, lvl);
+            } else if (element instanceof CtInvocation) {
+                return eval((CtInvocation) element, lvl);
+            } else {
+                log("cannot handle "+element.getClass()+ " "+element);
+                return null;
+            }
+        }
+
+        public String getResult() {
+            return result;
+        }
+
+        public String getErrorLog() {
+            return sb.toString();
+        }
+
+        protected Object eval(CtLiteral literal, int lvl) {
+            return literal.getValue();
+        }
+
+        protected Object eval(CtFieldRead frr, int lvl) {
+            CtFieldReference fr = frr.getVariable();
+            String d = "";
+            if (fr.isFinal()) {
+                d += "final ";
+            }
+            if (fr.isStatic()) {
+                d += "static ";
+            }
+            log(" getKey qualified name - "+fr.getQualifiedName());
+            CtField fld = fr.getDeclaration();
+            if (fr.isFinal() && fr.isStatic() && fld != null) {
+                CtExpression def = fld.getDefaultExpression();
+                if (def == null) throw new AssertionError();
+                log("  getKey default expression: "+def.getClass());
+                return this.evalWrk(def, lvl);
+            }
+            log(" getKey result: "+d+" "+fld);
+            return null;
+        }
+
+        protected Object eval(CtVariableRead vr, int lvl) {
+            CtVariableReference vref = vr.getVariable();
+            CtVariable var = vref.getDeclaration();
+            if (!var.getModifiers().contains(ModifierKind.FINAL)) {
+                log("non-final variable: "+var.getSimpleName());
+                return null;
+            }
+            CtExpression def = var.getDefaultExpression();
+            if (def == null) {
+                log("no initializer for variable: "+var.getSimpleName());
+                return null;
+            }
+            return evalWrk(def, lvl);
+        }
+
+        protected Object eval(CtUnaryOperator uo, int lvl) {
+            if (uo.getKind() != UnaryOperatorKind.NEG) {
+                log("cannot handle unary operator: "+uo.getKind());
+                return null;
+            }
+            Object arg = evalWrk(uo.getOperand(), lvl);
+            if (arg == null) return null;
+            if (arg instanceof Integer) {
+                return - ((Integer) arg);
+            } else if (arg instanceof Long) {
+                return - ((Long) arg);
+            } else if (arg instanceof Float) {
+                return - ((Float) arg);
+            } else if (arg instanceof Double) {
+                return - ((Double) arg);
+            } else {
+                log("cannot handle type "+arg.getClass()+" of value "+arg);
+                return null;
+            }
+        }
+
+        protected Object eval(CtBinaryOperator bo, int lvl) {
+            if (bo.getKind() != BinaryOperatorKind.PLUS) {
+                log("cannot handle binary operator: "+bo.getKind());
+                return null;
+            }
+            Object left = evalWrk(bo.getLeftHandOperand(), lvl);
+            if (left != null && left instanceof String) {
+                Object right = evalWrk(bo.getRightHandOperand(), lvl);
+                if (right != null && right instanceof String) {
+                    return ((String) left) + ((String) right);
+                }
+            }
+            return null;
+        }
+
+        protected Object eval(CtInvocation inv, int lvl) {
+            if (inv.toString().equals("(getClass().getName())")) {
+                return determineClass(inv).getCanonicalName();
+            }
+            CtExecutableReference eref = inv.getExecutable();
+            Method m = eref.getActualMethod();
+            if (marktr.equals(m)) {
+                List<CtExpression<?>> args = inv.getArguments();
+                CtExpression arg = args.get(0);
+                return this.evalWrk(arg, lvl);
+            }
+            log("cannot handle CtInvocation: "+inv);
+            return null;
+        }
+
+        protected void log(String msg) {
+            sb.append(msg + "\n");
+        }
+    }
+}

Property changes on: scripts/PreferencesCollector.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: build.xml
===================================================================
--- build.xml	(revision 11730)
+++ build.xml	(working copy)
@@ -22,8 +22,9 @@
         <property name="dist.dir" location="${base.dir}/dist"/>
         <property name="javacc.home" location="${base.dir}/tools"/>
         <property name="mapcss.dir" location="${src.dir}/org/openstreetmap/josm/gui/mappaint/mapcss"/>
-        <property name="proj-build.dir" location="${base.dir}/build2"/>
+        <property name="build-phase2.dir" location="${base.dir}/build2"/>
         <property name="epsg.output" location="${base.dir}/data/projection/custom-epsg"/>
+        <property name="einstein.output" location="${base.dir}/data/preferences-available.xml"/>
         <property name="groovy.jar" location="${base.dir}/tools/groovy-all-2.4.8.jar"/>
         <property name="error_prone_ant.jar" location="${base.dir}/tools/error_prone_ant-2.0.18.jar"/>
         <property name="javac.compiler" value="com.google.errorprone.ErrorProneAntCompilerAdapter" />
@@ -354,12 +355,13 @@
     </target>
     <target name="clean" depends="init-properties">
         <delete dir="${build.dir}"/>
-        <delete dir="${proj-build.dir}"/>
+        <delete dir="${build-phase2.dir}"/>
         <delete dir="${dist.dir}"/>
         <delete dir="${mapcss.dir}/parsergen"/>
         <delete file="${src.dir}/org/w3/_2001/xmlschema/Adapter1.java"/>
         <delete dir="${src.dir}/org/openstreetmap/josm/data/imagery/types"/>
         <delete file="${epsg.output}"/>
+        <delete file="${einstein.output}"/>
     </target>
     <macrodef name="init-test-preferences">
         <attribute name="testfamily"/>
@@ -790,9 +792,9 @@
     -->
     <target name="epsg-compile" depends="init-properties">
         <property name="proj-classpath" location="${build.dir}"/>
-        <mkdir dir="${proj-build.dir}"/>
-        <javac sourcepath="" srcdir="${base.dir}/scripts" failonerror="true"
-            destdir="${proj-build.dir}" target="1.8" source="1.8" debug="on"
+        <mkdir dir="${build-phase2.dir}"/>
+        <javac sourcepath="" srcdir="${base.dir}/scripts" includes="BuildProjectionDefinitions.java" failonerror="true"
+            destdir="${build-phase2.dir}" target="1.8" source="1.8" debug="on"
             includeantruntime="false" createMissingPackageInfoClass="false"
             encoding="UTF-8" classpath="${proj-classpath}">
         </javac>
@@ -807,9 +809,33 @@
             <classpath>
                 <pathelement path="${base.dir}"/>
                 <pathelement path="${proj-classpath}"/>
-                <pathelement path="${proj-build.dir}"/>
+                <pathelement path="${build-phase2.dir}"/>
             </classpath>
             <arg value="${base.dir}"/>
         </java>
     </target>
+    <target name="einstein-compile" depends="init-properties">
+        <mkdir dir="${build-phase2.dir}"/>
+        <javac sourcepath="" srcdir="${base.dir}/scripts" failonerror="true"
+            destdir="${build-phase2.dir}" target="1.8" source="1.8" debug="on"
+            includeantruntime="false" createMissingPackageInfoClass="false"
+            encoding="UTF-8">
+            <classpath>
+                <pathelement path="${base.dir}/tools/spoon-core-5.5.0-jar-with-dependencies.jar"/>
+                <pathelement path="${build.dir}"/>
+            </classpath>
+        </javac>
+    </target>
+    <target name="einstein" depends="einstein-compile">
+        <touch file="${einstein.output}"/>
+        <java classname="PreferencesCollector" failonerror="true" fork="true">
+            <sysproperty key="java.awt.headless" value="true"/>
+            <classpath>
+                <pathelement path="${base.dir}/tools/spoon-core-5.5.0-jar-with-dependencies.jar"/>
+                <pathelement path="${build.dir}"/>
+                <pathelement path="${build-phase2.dir}"/>
+            </classpath>
+            <arg value="${base.dir}"/>
+        </java>
+    </target>
 </project>
