Ticket #13318: patch-fix-13318.patch

File patch-fix-13318.patch, 58.6 KB (added by michael2402, 9 years ago)
  • src/org/openstreetmap/josm/Main.java

    diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
    index a2ac9c0..c9a3b56 100644
    a b import java.util.concurrent.ExecutionException;  
    3434import java.util.concurrent.ExecutorService;
    3535import java.util.concurrent.Executors;
    3636import java.util.concurrent.Future;
    37 import java.util.logging.Handler;
    38 import java.util.logging.Level;
    39 import java.util.logging.LogRecord;
    40 import java.util.logging.Logger;
    4137
    4238import javax.swing.Action;
    4339import javax.swing.InputMap;
    import org.openstreetmap.josm.data.projection.Projection;  
    7369import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
    7470import org.openstreetmap.josm.data.validation.OsmValidator;
    7571import org.openstreetmap.josm.gui.GettingStarted;
    76 import org.openstreetmap.josm.gui.MainApplication.Option;
    7772import org.openstreetmap.josm.gui.MainFrame;
    7873import org.openstreetmap.josm.gui.MainMenu;
    7974import org.openstreetmap.josm.gui.MainPanel;
    8075import org.openstreetmap.josm.gui.MapFrame;
    8176import org.openstreetmap.josm.gui.MapFrameListener;
     77import org.openstreetmap.josm.gui.ProgramArguments;
     78import org.openstreetmap.josm.gui.ProgramArguments.Option;
    8279import org.openstreetmap.josm.gui.io.SaveLayersDialog;
    8380import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    8481import org.openstreetmap.josm.gui.layer.Layer;
    import org.openstreetmap.josm.plugins.PluginHandler;  
    103100import org.openstreetmap.josm.tools.CheckParameterUtil;
    104101import org.openstreetmap.josm.tools.I18n;
    105102import org.openstreetmap.josm.tools.ImageProvider;
     103import org.openstreetmap.josm.tools.Logging;
    106104import org.openstreetmap.josm.tools.OpenBrowser;
    107105import org.openstreetmap.josm.tools.OsmUrlToBounds;
    108106import org.openstreetmap.josm.tools.PlatformHook;
    public abstract class Main {  
    218216
    219217    protected static final Map<String, Throwable> NETWORK_ERRORS = new HashMap<>();
    220218
    221     // First lines of last 5 error and warning messages, used for bug reports
    222     private static final List<String> ERRORS_AND_WARNINGS = Collections.<String>synchronizedList(new ArrayList<String>());
    223 
    224219    private static final Set<OnlineResource> OFFLINE_RESOURCES = EnumSet.noneOf(OnlineResource.class);
    225220
    226221    /**
    227222     * Logging level (5 = trace, 4 = debug, 3 = info, 2 = warn, 1 = error, 0 = none).
    228223     * @since 6248
     224     * @deprecated Use {@link Logging} class.
    229225     */
     226    @Deprecated
    230227    public static int logLevel = 3;
    231228
    232229    /**
    public abstract class Main {  
    235232     */
    236233    protected static final MainPanel mainPanel = new MainPanel(getLayerManager());
    237234
    238     private static void rememberWarnErrorMsg(String msg) {
    239         // Only remember first line of message
    240         int idx = msg.indexOf('\n');
    241         if (idx > 0) {
    242             ERRORS_AND_WARNINGS.add(msg.substring(0, idx));
    243         } else {
    244             ERRORS_AND_WARNINGS.add(msg);
    245         }
    246         // Only keep 10 lines to avoid memory leak
    247         while (ERRORS_AND_WARNINGS.size() > 10) {
    248             ERRORS_AND_WARNINGS.remove(0);
    249         }
    250     }
    251 
    252235    /**
    253236     * Replies the first lines of last 5 error and warning messages, used for bug reports
    254237     * @return the first lines of last 5 error and warning messages
    255238     * @since 7420
    256239     */
    257240    public static final Collection<String> getLastErrorAndWarnings() {
    258         return Collections.unmodifiableList(ERRORS_AND_WARNINGS);
     241        return Logging.getLastErrorAndWarnings();
    259242    }
    260243
    261244    /**
    public abstract class Main {  
    263246     * @since 8959
    264247     */
    265248    public static void clearLastErrorAndWarnings() {
    266         ERRORS_AND_WARNINGS.clear();
     249        Logging.clearLastErrorAndWarnings();
    267250    }
    268251
    269252    /**
    public abstract class Main {  
    272255     * @since 6248
    273256     */
    274257    public static void error(String msg) {
    275         if (logLevel < 1)
    276             return;
    277         if (msg != null && !msg.isEmpty()) {
    278             System.err.println(tr("ERROR: {0}", msg));
    279             rememberWarnErrorMsg("E: "+msg);
    280         }
     258        Logging.error(msg);
    281259    }
    282260
    283261    /**
    public abstract class Main {  
    285263     * @param msg The message to print.
    286264     */
    287265    public static void warn(String msg) {
    288         if (logLevel < 2)
    289             return;
    290         if (msg != null && !msg.isEmpty()) {
    291             System.err.println(tr("WARNING: {0}", msg));
    292             rememberWarnErrorMsg("W: "+msg);
    293         }
     266        Logging.warn(msg);
    294267    }
    295268
    296269    /**
    public abstract class Main {  
    298271     * @param msg The message to print.
    299272     */
    300273    public static void info(String msg) {
    301         if (logLevel < 3)
    302             return;
    303         if (msg != null && !msg.isEmpty()) {
    304             System.out.println(tr("INFO: {0}", msg));
    305         }
     274        Logging.info(msg);
    306275    }
    307276
    308277    /**
    public abstract class Main {  
    310279     * @param msg The message to print.
    311280     */
    312281    public static void debug(String msg) {
    313         if (logLevel < 4)
    314             return;
    315         if (msg != null && !msg.isEmpty()) {
    316             System.out.println(tr("DEBUG: {0}", msg));
    317         }
     282        Logging.debug(msg);
    318283    }
    319284
    320285    /**
    public abstract class Main {  
    322287     * @param msg The message to print.
    323288     */
    324289    public static void trace(String msg) {
    325         if (logLevel < 5)
    326             return;
    327         if (msg != null && !msg.isEmpty()) {
    328             System.out.print("TRACE: ");
    329             System.out.println(msg);
    330         }
     290        Logging.trace(msg);
    331291    }
    332292
    333293    /**
    public abstract class Main {  
    337297     * @since 6852
    338298     */
    339299    public static boolean isDebugEnabled() {
    340         return logLevel >= 4;
     300        return Logging.isLoggingEnabled(Logging.LEVEL_DEBUG);
    341301    }
    342302
    343303    /**
    public abstract class Main {  
    347307     * @since 6852
    348308     */
    349309    public static boolean isTraceEnabled() {
    350         return logLevel >= 5;
     310        return Logging.isLoggingEnabled(Logging.LEVEL_TRACE);
    351311    }
    352312
    353313    /**
    public abstract class Main {  
    358318     * @since 6248
    359319     */
    360320    public static void error(String msg, Object... objects) {
    361         error(MessageFormat.format(msg, objects));
     321        Logging.error(msg, objects);
    362322    }
    363323
    364324    /**
    public abstract class Main {  
    368328     * @param objects The objects to insert into format string.
    369329     */
    370330    public static void warn(String msg, Object... objects) {
    371         warn(MessageFormat.format(msg, objects));
     331        Logging.warn(msg, objects);
    372332    }
    373333
    374334    /**
    public abstract class Main {  
    378338     * @param objects The objects to insert into format string.
    379339     */
    380340    public static void info(String msg, Object... objects) {
    381         info(MessageFormat.format(msg, objects));
     341        Logging.info(msg, objects);
    382342    }
    383343
    384344    /**
    public abstract class Main {  
    388348     * @param objects The objects to insert into format string.
    389349     */
    390350    public static void debug(String msg, Object... objects) {
    391         debug(MessageFormat.format(msg, objects));
     351        Logging.debug(msg, objects);
    392352    }
    393353
    394354    /**
    public abstract class Main {  
    398358     * @param objects The objects to insert into format string.
    399359     */
    400360    public static void trace(String msg, Object... objects) {
    401         trace(MessageFormat.format(msg, objects));
     361        Logging.trace(msg, objects);
    402362    }
    403363
    404364    /**
    public abstract class Main {  
    407367     * @since 6248
    408368     */
    409369    public static void error(Throwable t) {
    410         error(t, true);
     370        Logging.logWithStackTrace(Logging.LEVEL_ERROR, t);
    411371    }
    412372
    413373    /**
    public abstract class Main {  
    416376     * @since 6248
    417377     */
    418378    public static void warn(Throwable t) {
    419         warn(t, true);
     379        Logging.logWithStackTrace(Logging.LEVEL_WARN, t);
    420380    }
    421381
    422382    /**
    public abstract class Main {  
    425385     * @since 10420
    426386     */
    427387    public static void debug(Throwable t) {
    428         debug(getErrorMessage(t));
     388        Logging.log(Logging.LEVEL_DEBUG, t);
    429389    }
    430390
    431391    /**
    public abstract class Main {  
    434394     * @since 10420
    435395     */
    436396    public static void trace(Throwable t) {
    437         trace(getErrorMessage(t));
     397        Logging.log(Logging.LEVEL_TRACE, t);
    438398    }
    439399
    440400    /**
    public abstract class Main {  
    444404     * @since 6642
    445405     */
    446406    public static void error(Throwable t, boolean stackTrace) {
    447         error(getErrorMessage(t));
    448407        if (stackTrace) {
    449             t.printStackTrace();
     408            Logging.log(Logging.LEVEL_ERROR, t);
     409        } else {
     410            Logging.logWithStackTrace(Logging.LEVEL_ERROR, t);
    450411        }
    451412    }
    452413
    public abstract class Main {  
    457418     * @since 10420
    458419     */
    459420    public static void error(Throwable t, String message) {
    460         warn(message + ' ' + getErrorMessage(t));
     421        Logging.log(Logging.LEVEL_ERROR, message, t);
    461422    }
    462423
    463424    /**
    public abstract class Main {  
    467428     * @since 6642
    468429     */
    469430    public static void warn(Throwable t, boolean stackTrace) {
    470         warn(getErrorMessage(t));
    471431        if (stackTrace) {
    472             t.printStackTrace();
     432            Logging.log(Logging.LEVEL_WARN, t);
     433        } else {
     434            Logging.logWithStackTrace(Logging.LEVEL_WARN, t);
    473435        }
    474436    }
    475437
    public abstract class Main {  
    480442     * @since 10420
    481443     */
    482444    public static void warn(Throwable t, String message) {
    483         warn(message + ' ' + getErrorMessage(t));
     445        Logging.log(Logging.LEVEL_WARN, message, t);
    484446    }
    485447
    486448    /**
    public abstract class Main {  
    490452     * @since 6642
    491453     */
    492454    public static String getErrorMessage(Throwable t) {
    493         if (t == null) {
    494             return null;
    495         }
    496         StringBuilder sb = new StringBuilder(t.getClass().getName());
    497         String msg = t.getMessage();
    498         if (msg != null) {
    499             sb.append(": ").append(msg.trim());
    500         }
    501         Throwable cause = t.getCause();
    502         if (cause != null && !cause.equals(t)) {
    503             sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause));
    504         }
    505         return sb.toString();
     455        return Logging.getErrorMessage(t);
    506456    }
    507457
    508458    /**
    public abstract class Main {  
    585535        isOpenjdk = System.getProperty("java.vm.name").toUpperCase(Locale.ENGLISH).indexOf("OPENJDK") != -1;
    586536        fileWatcher.start();
    587537
    588         new InitializationTask(tr("Executing platform startup hook")) {
    589             @Override
    590             public void initialize() {
    591                 platform.startupHook();
    592             }
    593         }.call();
     538        new InitializationTask(tr("Executing platform startup hook"), platform::startupHook).call();
    594539
    595         new InitializationTask(tr("Building main menu")) {
    596 
    597             @Override
    598             public void initialize() {
    599                 initializeMainWindow();
    600             }
    601         }.call();
     540        new InitializationTask(tr("Building main menu"), this::initializeMainWindow).call();
    602541
    603542        undoRedo.addCommandQueueListener(redoUndoListener);
    604543
    public abstract class Main {  
    611550        // contains several initialization tasks to be executed (in parallel) by a ExecutorService
    612551        List<Callable<Void>> tasks = new ArrayList<>();
    613552
    614         tasks.add(new InitializationTask(tr("Initializing OSM API")) {
    615 
    616             @Override
    617             public void initialize() {
     553        tasks.add(new InitializationTask(tr("Initializing OSM API"), () -> {
    618554                // We try to establish an API connection early, so that any API
    619555                // capabilities are already known to the editor instance. However
    620556                // if it goes wrong that's not critical at this stage.
    public abstract class Main {  
    623559                } catch (OsmTransferCanceledException | OsmApiInitializationException e) {
    624560                    Main.warn(getErrorMessage(Utils.getRootCause(e)));
    625561                }
    626             }
    627         });
    628 
    629         tasks.add(new InitializationTask(tr("Initializing validator")) {
     562            }));
    630563
    631             @Override
    632             public void initialize() {
    633                 OsmValidator.initialize();
    634             }
    635         });
     564        tasks.add(new InitializationTask(tr("Initializing validator"), OsmValidator::initialize));
    636565
    637         tasks.add(new InitializationTask(tr("Initializing presets")) {
     566        tasks.add(new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize));
    638567
    639             @Override
    640             public void initialize() {
    641                 TaggingPresets.initialize();
    642             }
    643         });
     568        tasks.add(new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize));
    644569
    645         tasks.add(new InitializationTask(tr("Initializing map styles")) {
    646 
    647             @Override
    648             public void initialize() {
    649                 MapPaintPreference.initialize();
    650             }
    651         });
    652 
    653         tasks.add(new InitializationTask(tr("Loading imagery preferences")) {
    654 
    655             @Override
    656             public void initialize() {
    657                 ImageryPreference.initialize();
    658             }
    659         });
     570        tasks.add(new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize));
    660571
    661572        try {
    662             final ExecutorService service = Executors.newFixedThreadPool(
     573            ExecutorService service = Executors.newFixedThreadPool(
    663574                    Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY));
    664575            for (Future<Void> i : service.invokeAll(tasks)) {
    665576                i.get();
    public abstract class Main {  
    672583        // hooks for the jmapviewer component
    673584        FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl);
    674585        FeatureAdapter.registerTranslationAdapter(I18n.getTranslationAdapter());
    675         FeatureAdapter.registerLoggingAdapter(name -> {
    676                 Logger logger = Logger.getAnonymousLogger();
    677                 logger.setUseParentHandlers(false);
    678                 logger.setLevel(Level.ALL);
    679                 if (logger.getHandlers().length == 0) {
    680                     logger.addHandler(new Handler() {
    681                         @Override
    682                         public void publish(LogRecord record) {
    683                             String msg = MessageFormat.format(record.getMessage(), record.getParameters());
    684                             if (record.getLevel().intValue() >= Level.SEVERE.intValue()) {
    685                                 Main.error(msg);
    686                             } else if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
    687                                 Main.warn(msg);
    688                             } else if (record.getLevel().intValue() >= Level.INFO.intValue()) {
    689                                 Main.info(msg);
    690                             } else if (record.getLevel().intValue() >= Level.FINE.intValue()) {
    691                                 Main.debug(msg);
    692                             } else {
    693                                 Main.trace(msg);
    694                             }
    695                         }
    696 
    697                         @Override
    698                         public void flush() {
    699                             // Do nothing
    700                         }
    701 
    702                         @Override
    703                         public void close() {
    704                             // Do nothing
    705                         }
    706                     });
    707                 }
    708                 return logger;
    709             });
     586        FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger());
    710587
    711         new InitializationTask(tr("Updating user interface")) {
    712 
    713             @Override
    714             public void initialize() {
    715                 toolbar.refreshToolbarControl();
    716                 toolbar.control.updateUI();
    717                 contentPanePrivate.updateUI();
    718             }
    719         }.call();
     588        new InitializationTask(tr("Updating user interface"), () -> {
     589            toolbar.refreshToolbarControl();
     590            toolbar.control.updateUI();
     591            contentPanePrivate.updateUI();
     592        }).call();
    720593    }
    721594
    722595    /**
    public abstract class Main {  
    727600        // can be implementd by subclasses
    728601    }
    729602
    730     private abstract static class InitializationTask implements Callable<Void> {
     603    private static class InitializationTask implements Callable<Void> {
    731604
    732605        private final String name;
     606        private Runnable task;
    733607
    734         protected InitializationTask(String name) {
     608        protected InitializationTask(String name, Runnable task) {
    735609            this.name = name;
     610            this.task = task;
    736611        }
    737612
    738         public abstract void initialize();
    739 
    740613        @Override
    741614        public Void call() {
    742615            Object status = null;
    743616            if (initListener != null) {
    744617                status = initListener.updateStatus(name);
    745618            }
    746             initialize();
     619            task.run();
    747620            if (initListener != null) {
    748621                initListener.finish(status);
    749622            }
    public abstract class Main {  
    965838     * Should be called before the main constructor to setup some parameter stuff
    966839     * @param args The parsed argument list.
    967840     */
    968     public static void preConstructorInit(Map<Option, Collection<String>> args) {
     841    public static void preConstructorInit(ProgramArguments args) {
    969842        ProjectionPreference.setProjection();
    970843
    971844        String defaultlaf = platform.getDefaultStyle();
    public abstract class Main {  
    1032905        }
    1033906    }
    1034907
    1035     protected static void postConstructorProcessCmdLine(Map<Option, Collection<String>> args) {
    1036         if (args.containsKey(Option.DOWNLOAD)) {
    1037             List<File> fileList = new ArrayList<>();
    1038             for (String s : args.get(Option.DOWNLOAD)) {
    1039                 DownloadParamType.paramType(s).download(s, fileList);
    1040             }
    1041             if (!fileList.isEmpty()) {
    1042                 OpenFileAction.openFiles(fileList, true);
    1043             }
     908    protected static void postConstructorProcessCmdLine(ProgramArguments args) {
     909        List<File> fileList = new ArrayList<>();
     910        for (String s : args.get(Option.DOWNLOAD)) {
     911            DownloadParamType.paramType(s).download(s, fileList);
    1044912        }
    1045         if (args.containsKey(Option.DOWNLOADGPS)) {
    1046             for (String s : args.get(Option.DOWNLOADGPS)) {
    1047                 DownloadParamType.paramType(s).downloadGps(s);
    1048             }
     913        if (!fileList.isEmpty()) {
     914            OpenFileAction.openFiles(fileList, true);
    1049915        }
    1050         if (args.containsKey(Option.SELECTION)) {
    1051             for (String s : args.get(Option.SELECTION)) {
    1052                 SearchAction.search(s, SearchAction.SearchMode.add);
    1053             }
     916        for (String s : args.get(Option.DOWNLOADGPS)) {
     917            DownloadParamType.paramType(s).downloadGps(s);
     918        }
     919        for (String s : args.get(Option.SELECTION)) {
     920            SearchAction.search(s, SearchAction.SearchMode.add);
    1054921        }
    1055922    }
    1056923
    public abstract class Main {  
    15031370        public static void setup() {
    15041371            if (!windowSwitchListeners.isEmpty()) {
    15051372                for (Window w : Window.getWindows()) {
    1506                     if (w.isShowing()) {
    1507                         if (!Arrays.asList(w.getWindowListeners()).contains(getInstance())) {
    1508                             w.addWindowListener(getInstance());
    1509                         }
     1373                    if (w.isShowing() && !Arrays.asList(w.getWindowListeners()).contains(getInstance())) {
     1374                        w.addWindowListener(getInstance());
    15101375                    }
    15111376                }
    15121377            }
  • src/org/openstreetmap/josm/gui/MainApplication.java

    diff --git a/src/org/openstreetmap/josm/gui/MainApplication.java b/src/org/openstreetmap/josm/gui/MainApplication.java
    index b65e16c..c730abf 100644
    a b import java.security.PermissionCollection;  
    2424import java.security.Permissions;
    2525import java.security.Policy;
    2626import java.security.cert.CertificateException;
    27 import java.util.ArrayList;
    2827import java.util.Arrays;
    2928import java.util.Collection;
    30 import java.util.EnumMap;
    3129import java.util.List;
    3230import java.util.Locale;
    33 import java.util.Map;
     31import java.util.Optional;
    3432import java.util.Set;
    3533import java.util.TreeSet;
     34import java.util.logging.Level;
    3635
    3736import javax.swing.JOptionPane;
    3837import javax.swing.RepaintManager;
    import org.openstreetmap.josm.actions.RestartAction;  
    4544import org.openstreetmap.josm.data.AutosaveTask;
    4645import org.openstreetmap.josm.data.CustomConfigurator;
    4746import org.openstreetmap.josm.data.Version;
     47import org.openstreetmap.josm.gui.ProgramArguments.Option;
    4848import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor;
    4949import org.openstreetmap.josm.gui.download.DownloadDialog;
    5050import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
    import org.openstreetmap.josm.plugins.PluginInformation;  
    6262import org.openstreetmap.josm.tools.FontsManager;
    6363import org.openstreetmap.josm.tools.HttpClient;
    6464import org.openstreetmap.josm.tools.I18n;
     65import org.openstreetmap.josm.tools.Logging;
    6566import org.openstreetmap.josm.tools.OsmUrlToBounds;
    6667import org.openstreetmap.josm.tools.PlatformHookWindows;
    6768import org.openstreetmap.josm.tools.Utils;
    6869import org.openstreetmap.josm.tools.WindowGeometry;
     70import org.openstreetmap.josm.tools.bugreport.BugReport;
    6971import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
    7072
    71 import gnu.getopt.Getopt;
    72 import gnu.getopt.LongOpt;
    73 
    7473/**
    7574 * Main window class application.
    7675 *
    public class MainApplication extends Main {  
    170169    }
    171170
    172171    /**
    173      * JOSM command line options.
    174      * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a>
    175      * @since 5279
    176      */
    177     public enum Option {
    178         /** --help|-h                                  Show this help */
    179         HELP(false),
    180         /** --version                                  Displays the JOSM version and exits */
    181         VERSION(false),
    182         /** --debug                                    Print debugging messages to console */
    183         DEBUG(false),
    184         /** --trace                                    Print detailed debugging messages to console */
    185         TRACE(false),
    186         /** --language=&lt;language&gt;                Set the language */
    187         LANGUAGE(true),
    188         /** --reset-preferences                        Reset the preferences to default */
    189         RESET_PREFERENCES(false),
    190         /** --load-preferences=&lt;url-to-xml&gt;      Changes preferences according to the XML file */
    191         LOAD_PREFERENCES(true),
    192         /** --set=&lt;key&gt;=&lt;value&gt;            Set preference key to value */
    193         SET(true),
    194         /** --geometry=widthxheight(+|-)x(+|-)y        Standard unix geometry argument */
    195         GEOMETRY(true),
    196         /** --no-maximize                              Do not launch in maximized mode */
    197         NO_MAXIMIZE(false),
    198         /** --maximize                                 Launch in maximized mode */
    199         MAXIMIZE(false),
    200         /** --download=minlat,minlon,maxlat,maxlon     Download the bounding box <br>
    201          *  --download=&lt;URL&gt;                     Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
    202          *  --download=&lt;filename&gt;                Open a file (any file type that can be opened with File/Open) */
    203         DOWNLOAD(true),
    204         /** --downloadgps=minlat,minlon,maxlat,maxlon  Download the bounding box as raw GPS <br>
    205          *  --downloadgps=&lt;URL&gt;                  Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
    206         DOWNLOADGPS(true),
    207         /** --selection=&lt;searchstring&gt;           Select with the given search */
    208         SELECTION(true),
    209         /** --offline=&lt;osm_api|josm_website|all&gt; Disable access to the given resource(s), delimited by comma */
    210         OFFLINE(true),
    211         /** --skip-plugins */
    212         SKIP_PLUGINS(false);
    213 
    214         private final String name;
    215         private final boolean requiresArg;
    216 
    217         Option(boolean requiresArgument) {
    218             this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-');
    219             this.requiresArg = requiresArgument;
    220         }
    221 
    222         /**
    223          * Replies the option name
    224          * @return The option name, in lowercase
    225          */
    226         public String getName() {
    227             return name;
    228         }
    229 
    230         /**
    231          * Determines if this option requires an argument.
    232          * @return {@code true} if this option requires an argument, {@code false} otherwise
    233          */
    234         public boolean requiresArgument() {
    235             return requiresArg;
    236         }
    237     }
    238 
    239     /**
    240      * Builds the command-line argument map.
    241      * @param args command-line arguments array
    242      * @return command-line argument map
    243      */
    244     public static Map<Option, Collection<String>> buildCommandLineArgumentMap(String ... args) {
    245 
    246         List<LongOpt> los = new ArrayList<>();
    247         for (Option o : Option.values()) {
    248             los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0));
    249         }
    250 
    251         Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()]));
    252 
    253         Map<Option, Collection<String>> argMap = new EnumMap<>(Option.class);
    254 
    255         int c;
    256         while ((c = g.getopt()) != -1) {
    257             Option opt;
    258             switch (c) {
    259                 case 'h':
    260                     opt = Option.HELP;
    261                     break;
    262                 case 'v':
    263                     opt = Option.VERSION;
    264                     break;
    265                 case 0:
    266                     opt = Option.values()[g.getLongind()];
    267                     break;
    268                 default:
    269                     opt = null;
    270             }
    271             if (opt != null) {
    272                 Collection<String> values = argMap.get(opt);
    273                 if (values == null) {
    274                     values = new ArrayList<>();
    275                     argMap.put(opt, values);
    276                 }
    277                 values.add(g.getOptarg());
    278             } else
    279                 throw new IllegalArgumentException("Invalid option: "+c);
    280         }
    281         // positional arguments are a shortcut for the --download ... option
    282         for (int i = g.getOptind(); i < args.length; ++i) {
    283             Collection<String> values = argMap.get(Option.DOWNLOAD);
    284             if (values == null) {
    285                 values = new ArrayList<>();
    286                 argMap.put(Option.DOWNLOAD, values);
    287             }
    288             values.add(args[i]);
    289         }
    290 
    291         return argMap;
    292     }
    293 
    294     /**
    295172     * Main application Startup
    296173     * @param argArray Command-line arguments
    297174     */
    public class MainApplication extends Main {  
    299176        I18n.init();
    300177
    301178        // construct argument table
    302         Map<Option, Collection<String>> args = null;
     179        ProgramArguments args = null;
    303180        try {
    304             args = buildCommandLineArgumentMap(argArray);
     181            args = new ProgramArguments(argArray);
    305182        } catch (IllegalArgumentException e) {
    306183            System.exit(1);
    307184            return;
    308185        }
    309186
    310         final boolean languageGiven = args.containsKey(Option.LANGUAGE);
     187        Level logLevel = args.getLogLevel();
     188        Logging.setLogLevel(logLevel);
     189        Main.info(tr("Log level is at ", logLevel));
    311190
    312         if (languageGiven) {
    313             I18n.set(args.get(Option.LANGUAGE).iterator().next());
    314         }
     191        Optional<String> language = args.getSingle(Option.LANGUAGE);
     192        I18n.set(language.orElse(null));
    315193
    316194        initApplicationPreferences();
    317195
    public class MainApplication extends Main {  
    339217
    340218        Main.COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray));
    341219
    342         if (args.containsKey(Option.VERSION)) {
     220        if (args.showVersion()) {
    343221            System.out.println(Version.getInstance().getAgentString());
    344222            System.exit(0);
    345         }
    346 
    347         if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) {
    348             // Enable JOSM debug level
    349             logLevel = 4;
    350             Main.info(tr("Printing debugging messages to console"));
     223        } else if (args.showHelp()) {
     224            showHelp();
     225            System.exit(0);
    351226        }
    352227
    353228        boolean skipLoadingPlugins = false;
    354         if (args.containsKey(Option.SKIP_PLUGINS)) {
     229        if (args.hasOption(Option.SKIP_PLUGINS)) {
    355230            skipLoadingPlugins = true;
    356231            Main.info(tr("Plugin loading skipped"));
    357232        }
    358233
    359         if (args.containsKey(Option.TRACE)) {
    360             // Enable JOSM debug level
    361             logLevel = 5;
     234        if (Logging.isLoggingEnabled(Logging.LEVEL_TRACE)) {
    362235            // Enable debug in OAuth signpost via system preference, but only at trace level
    363236            Utils.updateSystemProperty("debug", "true");
    364237            Main.info(tr("Enabled detailed debug level (trace)"));
    365238        }
    366239
    367         Main.pref.init(args.containsKey(Option.RESET_PREFERENCES));
     240        Main.pref.init(args.hasOption(Option.RESET_PREFERENCES));
    368241
    369         if (args.containsKey(Option.SET)) {
    370             for (String i : args.get(Option.SET)) {
    371                 String[] kv = i.split("=", 2);
    372                 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
    373             }
    374         }
     242        args.getPreferencesToSet().forEach(Main.pref::put);
    375243
    376         if (!languageGiven) {
     244        if (!language.isPresent()) {
    377245            I18n.set(Main.pref.get("language", null));
    378246        }
    379247        Main.pref.updateSystemProperties();
    public class MainApplication extends Main {  
    381249        checkIPv6();
    382250
    383251        // asking for help? show help and exit
    384         if (args.containsKey(Option.HELP)) {
     252        if (args.hasOption(Option.HELP)) {
    385253            showHelp();
    386254            System.exit(0);
    387255        }
    public class MainApplication extends Main {  
    395263        I18n.setupLanguageFonts();
    396264
    397265        WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry",
    398                 args.containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null,
    399                 !args.containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));
     266                args.getSingle(Option.GEOMETRY).orElse(null),
     267                !args.hasOption(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));
    400268        final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry);
    401269        Main.parent = mainFrame;
    402270
    403         if (args.containsKey(Option.LOAD_PREFERENCES)) {
     271        if (args.hasOption(Option.LOAD_PREFERENCES)) {
    404272            CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
    405273            for (String i : args.get(Option.LOAD_PREFERENCES)) {
    406274                info("Reading preferences from " + i);
    407275                try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) {
    408276                    config.openAndReadXML(is);
    409277                } catch (IOException ex) {
    410                     throw new RuntimeException(ex);
     278                    throw BugReport.intercept(ex).put("file", i);
    411279                }
    412280            }
    413281        }
    public class MainApplication extends Main {  
    470338        Main.MasterWindowListener.setup();
    471339
    472340        boolean maximized = Main.pref.getBoolean("gui.maximized", false);
    473         if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
     341        if ((!args.hasOption(Option.NO_MAXIMIZE) && maximized) || args.hasOption(Option.MAXIMIZE)) {
    474342            mainFrame.setMaximized(true);
    475343        }
    476344        if (main.menu.fullscreenToggleAction != null) {
    public class MainApplication extends Main {  
    528396        toolbar.refreshToolbarControl();
    529397    }
    530398
    531     private static void processOffline(Map<Option, Collection<String>> args) {
    532         if (args.containsKey(Option.OFFLINE)) {
    533             for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) {
     399    private static void processOffline(ProgramArguments args) {
     400        for (String offlineNames : args.get(Option.OFFLINE)) {
     401            for (String s : offlineNames.split(",")) {
    534402                try {
    535403                    Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH)));
    536404                } catch (IllegalArgumentException e) {
    public class MainApplication extends Main {  
    540408                    return;
    541409                }
    542410            }
    543             Set<OnlineResource> offline = Main.getOfflineResources();
    544             if (!offline.isEmpty()) {
    545                 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
    546                         "JOSM is running in offline mode. These resources will not be available: {0}",
    547                         offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
    548             }
     411        }
     412        Set<OnlineResource> offline = Main.getOfflineResources();
     413        if (!offline.isEmpty()) {
     414            Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
     415                    "JOSM is running in offline mode. These resources will not be available: {0}",
     416                    offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
    549417        }
    550418    }
    551419
    public class MainApplication extends Main {  
    600468
    601469    private static class GuiFinalizationWorker implements Runnable {
    602470
    603         private final Map<Option, Collection<String>> args;
     471        private final ProgramArguments args;
    604472        private final DefaultProxySelector proxySelector;
    605473
    606         GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) {
     474        GuiFinalizationWorker(ProgramArguments args, DefaultProxySelector proxySelector) {
    607475            this.args = args;
    608476            this.proxySelector = proxySelector;
    609477        }
  • new file src/org/openstreetmap/josm/gui/ProgramArguments.java

    diff --git a/src/org/openstreetmap/josm/gui/ProgramArguments.java b/src/org/openstreetmap/josm/gui/ProgramArguments.java
    new file mode 100644
    index 0000000..cf446f2
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui;
     3
     4import java.util.ArrayList;
     5import java.util.Collection;
     6import java.util.Collections;
     7import java.util.EnumMap;
     8import java.util.HashMap;
     9import java.util.List;
     10import java.util.Locale;
     11import java.util.Map;
     12import java.util.Optional;
     13import java.util.logging.Level;
     14import java.util.stream.Stream;
     15
     16import org.openstreetmap.josm.tools.Logging;
     17
     18import gnu.getopt.Getopt;
     19import gnu.getopt.LongOpt;
     20
     21/**
     22 * This class holds the arguments passed on to Main.
     23 * @author Michael Zangl
     24 * @since xxx
     25 */
     26public class ProgramArguments {
     27
     28    /**
     29     * JOSM command line options.
     30     * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a>
     31     * @since xxx
     32     */
     33    public enum Option {
     34        /** --help|-h                                  Show this help */
     35        HELP(false),
     36        /** --version                                  Displays the JOSM version and exits */
     37        VERSION(false),
     38        /** --debug                                    Print debugging messages to console */
     39        DEBUG(false),
     40        /** --trace                                    Print detailed debugging messages to console */
     41        TRACE(false),
     42        /** --language=&lt;language&gt;                Set the language */
     43        LANGUAGE(true),
     44        /** --reset-preferences                        Reset the preferences to default */
     45        RESET_PREFERENCES(false),
     46        /** --load-preferences=&lt;url-to-xml&gt;      Changes preferences according to the XML file */
     47        LOAD_PREFERENCES(true),
     48        /** --set=&lt;key&gt;=&lt;value&gt;            Set preference key to value */
     49        SET(true),
     50        /** --geometry=widthxheight(+|-)x(+|-)y        Standard unix geometry argument */
     51        GEOMETRY(true),
     52        /** --no-maximize                              Do not launch in maximized mode */
     53        NO_MAXIMIZE(false),
     54        /** --maximize                                 Launch in maximized mode */
     55        MAXIMIZE(false),
     56        /** --download=minlat,minlon,maxlat,maxlon     Download the bounding box <br>
     57         *  --download=&lt;URL&gt;                     Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
     58         *  --download=&lt;filename&gt;                Open a file (any file type that can be opened with File/Open) */
     59        DOWNLOAD(true),
     60        /** --downloadgps=minlat,minlon,maxlat,maxlon  Download the bounding box as raw GPS <br>
     61         *  --downloadgps=&lt;URL&gt;                  Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
     62        DOWNLOADGPS(true),
     63        /** --selection=&lt;searchstring&gt;           Select with the given search */
     64        SELECTION(true),
     65        /** --offline=&lt;osm_api|josm_website|all&gt; Disable access to the given resource(s), delimited by comma */
     66        OFFLINE(true),
     67        /** --skip-plugins */
     68        SKIP_PLUGINS(false);
     69
     70        private final String name;
     71        private final boolean requiresArg;
     72
     73        Option(boolean requiresArgument) {
     74            this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-');
     75            this.requiresArg = requiresArgument;
     76        }
     77
     78        /**
     79         * Replies the option name
     80         * @return The option name, in lowercase
     81         */
     82        public String getName() {
     83            return name;
     84        }
     85
     86        /**
     87         * Determines if this option requires an argument.
     88         * @return {@code true} if this option requires an argument, {@code false} otherwise
     89         */
     90        public boolean requiresArgument() {
     91            return requiresArg;
     92        }
     93
     94        LongOpt toLongOpt() {
     95            return new LongOpt(getName(), requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0);
     96        }
     97    }
     98
     99    private final Map<Option, List<String>> argMap = new EnumMap<>(Option.class);
     100
     101    /**
     102     * Construct the program arguments object
     103     * @param args The args passed to main.
     104     */
     105    public ProgramArguments(String[] args) {
     106        Stream.of(Option.values()).forEach(o -> argMap.put(o, new ArrayList<>()));
     107
     108        buildCommandLineArgumentMap(args);
     109    }
     110
     111    /**
     112     * Builds the command-line argument map.
     113     * @param args command-line arguments array
     114     */
     115    private void buildCommandLineArgumentMap(String ... args) {
     116        LongOpt[] los = Stream.of(Option.values()).map(Option::toLongOpt).toArray(i -> new LongOpt[i]);
     117
     118        Getopt g = new Getopt("JOSM", args, "hv", los);
     119
     120        int c;
     121        while ((c = g.getopt()) != -1) {
     122            Option opt;
     123            switch (c) {
     124            case 'h':
     125                opt = Option.HELP;
     126                break;
     127            case 'v':
     128                opt = Option.VERSION;
     129                break;
     130            case 0:
     131                opt = Option.values()[g.getLongind()];
     132                break;
     133            default:
     134                opt = null;
     135            }
     136            if (opt != null) {
     137                addOption(opt, g.getOptarg());
     138            } else
     139                throw new IllegalArgumentException("Invalid option: "+c);
     140        }
     141        // positional arguments are a shortcut for the --download ... option
     142        for (int i = g.getOptind(); i < args.length; ++i) {
     143            addOption(Option.DOWNLOAD, args[i]);
     144        }
     145    }
     146
     147    private void addOption(Option opt, String optarg) {
     148        argMap.get(opt).add(optarg);
     149    }
     150
     151    /**
     152     * Gets a single argument (the first) that was given for the given option.
     153     * @param option The option to search
     154     * @return The argument as optional value.
     155     */
     156    public Optional<String> getSingle(Option option) {
     157        return get(option).stream().findFirst();
     158    }
     159
     160    /**
     161     * Gets all values that are given for a given option
     162     * @param option The option
     163     * @return The values that were given. May be empty.
     164     */
     165    public Collection<String> get(Option option) {
     166        return Collections.unmodifiableList(argMap.get(option));
     167    }
     168
     169    /**
     170     * Test if a given option was used by the user.
     171     * @param option The option to test for
     172     * @return <code>true</code> if the user used it.
     173     */
     174    public boolean hasOption(Option option) {
     175        return !get(option).isEmpty();
     176    }
     177
     178    /**
     179     * Helper method to indicate if version should be displayed.
     180     * @return <code>true</code> to display version
     181     */
     182    public boolean showVersion() {
     183        return hasOption(Option.VERSION);
     184    }
     185
     186    /**
     187     * Helper method to indicate if help should be displayed.
     188     * @return <code>true</code> to display version
     189     */
     190    public boolean showHelp() {
     191        return !get(Option.HELP).isEmpty();
     192    }
     193
     194    /**
     195     * Get the log level the user wants us to use.
     196     * @return The log level.
     197     */
     198    public Level getLogLevel() {
     199        if (hasOption(Option.TRACE)) {
     200            return Logging.LEVEL_TRACE;
     201        } else if (hasOption(Option.DEBUG)) {
     202            return Logging.LEVEL_DEBUG;
     203        } else {
     204            return Logging.LEVEL_INFO;
     205        }
     206    }
     207
     208    /**
     209     * Gets a map of all preferences the user wants to set.
     210     * @return The preferences to set. It contains null values for preferences to unset
     211     */
     212    public Map<String, String> getPreferencesToSet() {
     213        HashMap<String, String> map = new HashMap<>();
     214        get(Option.SET).stream().map(i -> i.split("=", 2)).forEach(kv -> map.put(kv[0], getValue(kv)));
     215        return map;
     216    }
     217
     218    private static String getValue(String[] kv) {
     219        if (kv.length < 2) {
     220            return "";
     221        } else if ("null".equals(kv[1])) {
     222            return null;
     223        } else {
     224            return kv[1];
     225        }
     226    }
     227}
  • new file src/org/openstreetmap/josm/tools/Logging.java

    diff --git a/src/org/openstreetmap/josm/tools/Logging.java b/src/org/openstreetmap/josm/tools/Logging.java
    new file mode 100644
    index 0000000..db6855f
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.tools;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.io.OutputStream;
     7import java.io.PrintWriter;
     8import java.io.StringWriter;
     9import java.text.MessageFormat;
     10import java.util.ArrayList;
     11import java.util.Arrays;
     12import java.util.List;
     13import java.util.function.Supplier;
     14import java.util.logging.ConsoleHandler;
     15import java.util.logging.Handler;
     16import java.util.logging.Level;
     17import java.util.logging.LogRecord;
     18import java.util.logging.Logger;
     19
     20import org.openstreetmap.josm.tools.bugreport.BugReport;
     21
     22/**
     23 * This class contains utility methods to log errors and warnings.
     24 * <p>
     25 * There are multiple log levels supported.
     26 * @author Michael Zangl
     27 * @since xxx
     28 */
     29public final class Logging {
     30    /**
     31     * The josm internal log level indicating a severe error in the application that usually leads to a crash.
     32     */
     33    public static final Level LEVEL_ERROR = Level.SEVERE;
     34    /**
     35     * The josm internal log level to use when something that may lead to a crash or wrong behaviour has happened.
     36     */
     37    public static final Level LEVEL_WARN = Level.WARNING;
     38    /**
     39     * The josm internal log level to use for important events that will be useful when debugging problems
     40     */
     41    public static final Level LEVEL_INFO = Level.INFO;
     42    /**
     43     * The josm internal log level to print debug output
     44     */
     45    public static final Level LEVEL_DEBUG = Level.FINE;
     46    /**
     47     * The finest log level josm supports. This lets josm print a lot of debug output.
     48     */
     49    public static final Level LEVEL_TRACE = Level.FINEST;
     50    private static final Logger LOGGER = Logger.getAnonymousLogger();
     51    private static final RememberWarningHandler WARNINGS = new RememberWarningHandler();
     52
     53    static {
     54        LOGGER.setLevel(Level.ALL);
     55        LOGGER.setUseParentHandlers(false);
     56
     57        ConsoleHandler stderr = new ConsoleHandler();
     58        LOGGER.addHandler(stderr);
     59        stderr.setLevel(LEVEL_WARN);
     60
     61        ConsoleHandler stdout = new ConsoleHandler() {
     62            @Override
     63            protected synchronized void setOutputStream(OutputStream out) {
     64                // overwrite output stream.
     65                super.setOutputStream(System.out);
     66            }
     67
     68            @Override
     69            public void publish(LogRecord record) {
     70                if (!stderr.isLoggable(record)) {
     71                    super.publish(record);
     72                }
     73            }
     74        };
     75        LOGGER.addHandler(stdout);
     76        stdout.setLevel(Level.ALL);
     77
     78        LOGGER.addHandler(WARNINGS);
     79    }
     80
     81    private Logging() {
     82        // hide
     83    }
     84
     85    /**
     86     * Set the global log level.
     87     * @param level The log level to use
     88     */
     89    public static void setLogLevel(Level level) {
     90        LOGGER.setLevel(level);
     91    }
     92
     93    /**
     94     * Prints an error message if logging is on.
     95     * @param message The message to print.
     96     */
     97    public static void error(String message) {
     98        logPrivate(LEVEL_ERROR, message);
     99    }
     100
     101    /**
     102     * Prints a formatted error message if logging is on. Calls {@link MessageFormat#format}
     103     * function to format text.
     104     * @param pattern The formatted message to print.
     105     * @param args The objects to insert into format string.
     106     */
     107    public static void error(String pattern, Object... args) {
     108        logPrivate(LEVEL_ERROR, pattern, args);
     109    }
     110
     111    /**
     112     * Prints a warning message if logging is on.
     113     * @param message The message to print.
     114     */
     115    public static void warn(String message) {
     116        logPrivate(LEVEL_WARN, message);
     117    }
     118
     119    /**
     120     * Prints a formatted warning message if logging is on. Calls {@link MessageFormat#format}
     121     * function to format text.
     122     * @param pattern The formatted message to print.
     123     * @param args The objects to insert into format string.
     124     */
     125    public static void warn(String pattern, Object... args) {
     126        logPrivate(LEVEL_WARN, pattern, args);
     127    }
     128
     129    /**
     130     * Prints a info message if logging is on.
     131     * @param message The message to print.
     132     */
     133    public static void info(String message) {
     134        logPrivate(LEVEL_INFO, message);
     135    }
     136
     137    /**
     138     * Prints a formatted info message if logging is on. Calls {@link MessageFormat#format}
     139     * function to format text.
     140     * @param pattern The formatted message to print.
     141     * @param args The objects to insert into format string.
     142     */
     143    public static void info(String pattern, Object... args) {
     144        logPrivate(LEVEL_INFO, pattern, args);
     145    }
     146
     147    /**
     148     * Prints a debug message if logging is on.
     149     * @param message The message to print.
     150     */
     151    public static void debug(String message) {
     152        logPrivate(LEVEL_DEBUG, message);
     153    }
     154
     155    /**
     156     * Prints a formatted debug message if logging is on. Calls {@link MessageFormat#format}
     157     * function to format text.
     158     * @param pattern The formatted message to print.
     159     * @param args The objects to insert into format string.
     160     */
     161    public static void debug(String pattern, Object... args) {
     162        logPrivate(LEVEL_DEBUG, pattern, args);
     163    }
     164
     165    /**
     166     * Prints a trace message if logging is on.
     167     * @param message The message to print.
     168     */
     169    public static void trace(String message) {
     170        logPrivate(LEVEL_TRACE, message);
     171    }
     172
     173    /**
     174     * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format}
     175     * function to format text.
     176     * @param pattern The formatted message to print.
     177     * @param args The objects to insert into format string.
     178     */
     179    public static void trace(String pattern, Object... args) {
     180        logPrivate(LEVEL_TRACE, pattern, args);
     181    }
     182
     183    /**
     184     * Logs a throwable that happened.
     185     * @param level The level.
     186     * @param t The throwable that should be logged.
     187     */
     188    public static void log(Level level, Throwable t) {
     189        logPrivate(level, () -> getErrorLog(null, t));
     190    }
     191
     192    /**
     193     * Logs a throwable that happened.
     194     * @param level The level.
     195     * @param message An additional error message
     196     * @param t The throwable that caused the message
     197     */
     198    public static void log(Level level, String message, Throwable t) {
     199        logPrivate(level, () -> getErrorLog(message, t));
     200    }
     201
     202    /**
     203     * Logs a throwable that happened. Adds the stack trace to the log.
     204     * @param level The level.
     205     * @param t The throwable that should be logged.
     206     */
     207    public static void logWithStackTrace(Level level, Throwable t) {
     208        logPrivate(level, () -> getErrorLogWithStack(null, t));
     209    }
     210
     211    /**
     212     * Logs a throwable that happened. Adds the stack trace to the log.
     213     * @param level The level.
     214     * @param message An additional error message
     215     * @param t The throwable that should be logged.
     216     */
     217    public static void logWithStackTrace(Level level, String message, Throwable t) {
     218        logPrivate(level, () -> getErrorLogWithStack(message, t));
     219    }
     220
     221    private static void logPrivate(Level level, String pattern, Object... args) {
     222        logPrivate(level, () -> MessageFormat.format(pattern, args));
     223    }
     224
     225    private static void logPrivate(Level level, String message) {
     226        logPrivate(level, () -> message);
     227    }
     228
     229    private static void logPrivate(Level level, Supplier<String> supplier) {
     230        // all log methods immeadiately call one of the logPrivate methods.
     231        if (LOGGER.isLoggable(level)) {
     232            StackTraceElement callingMethod = BugReport.getCallingMethod(1, Logging.class.getName(), name -> !"logPrivate".equals(name));
     233            LOGGER.logp(level, callingMethod.getClassName(), callingMethod.getMethodName(), supplier);
     234        }
     235    }
     236
     237    /**
     238     * Tests if a given log level is enabled. This can be used to avoid constructing debug data if required.
     239     *
     240     * For formatting text, you should use the {@link #debug(String, Object...)} message
     241     * @param level A lvele constant. You can e.g. use {@link Logging#LEVEL_ERROR}
     242     * @return <code>true</code> if debug is enabled.
     243     */
     244    public static boolean isLoggingEnabled(Level level) {
     245        return LOGGER.isLoggable(level);
     246    }
     247
     248    private static String getErrorLog(String message, Throwable t) {
     249        StringBuilder sb = new StringBuilder();
     250        if (message != null) {
     251            sb.append(message).append(": ");
     252        }
     253        sb.append(getErrorMessage(t));
     254        return sb.toString();
     255    }
     256
     257    private static String getErrorLogWithStack(String message, Throwable t) {
     258        StringWriter sb = new StringWriter();
     259        sb.append(getErrorLog(message, t));
     260        sb.append('\n');
     261        t.printStackTrace(new PrintWriter(sb));
     262        return sb.toString();
     263    }
     264
     265    /**
     266     * Returns a human-readable message of error, also usable for developers.
     267     * @param t The error
     268     * @return The human-readable error message
     269     */
     270    public static String getErrorMessage(Throwable t) {
     271        if (t == null) {
     272            return "(no error)";
     273        }
     274        StringBuilder sb = new StringBuilder(t.getClass().getName());
     275        String msg = t.getMessage();
     276        if (msg != null) {
     277            sb.append(": ").append(msg.trim());
     278        }
     279        Throwable cause = t.getCause();
     280        if (cause != null && !cause.equals(t)) {
     281            sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause));
     282        }
     283        return sb.toString();
     284    }
     285
     286    /**
     287     * Clear the list of last warnings
     288     */
     289    public static void clearLastErrorAndWarnings() {
     290        WARNINGS.clear();
     291    }
     292
     293    /**
     294     * Get the last error and warning messages in the order in which they were received.
     295     * @return The last errors and warnings.
     296     */
     297    public static List<String> getLastErrorAndWarnings() {
     298        return WARNINGS.getMessages();
     299    }
     300
     301    /**
     302     * Provides direct access to the logger used. Use of methods like {@link #warn(String)} is prefered.
     303     * @return The logger
     304     */
     305    public static Logger getLogger() {
     306        return LOGGER;
     307    }
     308
     309    private static class RememberWarningHandler extends Handler {
     310        private final String[] log = new String[10];
     311        private int messagesLogged;
     312
     313        RememberWarningHandler() {
     314            setLevel(LEVEL_WARN);
     315        }
     316
     317        synchronized void clear() {
     318            messagesLogged = 0;
     319            Arrays.fill(log, null);
     320        }
     321
     322        @Override
     323        public synchronized void publish(LogRecord record) {
     324            if (!isLoggable(record)) {
     325                return;
     326            }
     327
     328            String msg = getPrefix(record) + record.getMessage();
     329
     330            // Only remember first line of message
     331            int idx = msg.indexOf('\n');
     332            if (idx > 0) {
     333                msg = msg.substring(0, idx);
     334            }
     335            log[messagesLogged % log.length] = msg;
     336            messagesLogged++;
     337        }
     338
     339        private static String getPrefix(LogRecord record) {
     340            if (record.getLevel().equals(LEVEL_WARN)) {
     341                return "W: ";
     342            } else {
     343                // worse than warn
     344                return "E: ";
     345            }
     346        }
     347
     348        synchronized List<String> getMessages() {
     349            List<String> logged = Arrays.asList(log);
     350            ArrayList<String> res = new ArrayList<>();
     351            int logOffset = messagesLogged % log.length;
     352            if (messagesLogged > logOffset) {
     353                res.addAll(logged.subList(logOffset, log.length));
     354            }
     355            res.addAll(logged.subList(0, logOffset));
     356            return res;
     357        }
     358
     359        @Override
     360        public synchronized void flush() {
     361            // nothing to do
     362        }
     363
     364        @Override
     365        public void close() {
     366            // nothing to do
     367        }
     368    }
     369}
  • src/org/openstreetmap/josm/tools/bugreport/BugReport.java

    diff --git a/src/org/openstreetmap/josm/tools/bugreport/BugReport.java b/src/org/openstreetmap/josm/tools/bugreport/BugReport.java
    index d47586f..426cb59 100644
    a b import java.io.PrintWriter;  
    55import java.io.Serializable;
    66import java.io.StringWriter;
    77import java.util.concurrent.CopyOnWriteArrayList;
     8import java.util.function.Predicate;
    89
    910import org.openstreetmap.josm.actions.ShowStatusReportAction;
    1011
    public final class BugReport implements Serializable {  
    180181     * @return The method name.
    181182     */
    182183    public static String getCallingMethod(int offset) {
    183         StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    184184        String className = BugReport.class.getName();
     185        String methodName = "getCallingMethod";
     186        StackTraceElement found = getCallingMethod(offset, className, methodName::equals);
     187        if (found != null) {
     188            return found.getClassName().replaceFirst(".*\\.", "") + '#' + found.getMethodName();
     189        } else {
     190            return "?";
     191        }
     192    }
     193
     194    /**
     195     * Find the method that called the given method on the current stack trace.
     196     * @param offset
     197     *           How many methods to look back in the stack trace. 1 gives the method calling this method, 0 gives you getCallingMethod().
     198     * @param className The name of the class to search for
     199     * @param methodName The name of the method to search for
     200     * @return The class and method name or null if it is unknown.
     201     */
     202    public static StackTraceElement getCallingMethod(int offset, String className, Predicate<String> methodName) {
     203        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    185204        for (int i = 0; i < stackTrace.length - offset; i++) {
    186205            StackTraceElement element = stackTrace[i];
    187             if (className.equals(element.getClassName()) && "getCallingMethod".equals(element.getMethodName())) {
     206            if (className.equals(element.getClassName()) && methodName.test(element.getMethodName())) {
    188207                StackTraceElement toReturn = stackTrace[i + offset];
    189                 return toReturn.getClassName().replaceFirst(".*\\.", "") + '#' + toReturn.getMethodName();
     208                return toReturn;
    190209            }
    191210        }
    192         return "?";
     211        return null;
    193212    }
    194213
    195214    /**
  • test/unit/org/openstreetmap/josm/JOSMFixture.java

    diff --git a/test/unit/org/openstreetmap/josm/JOSMFixture.java b/test/unit/org/openstreetmap/josm/JOSMFixture.java
    index 5e31aa7..7596606 100644
    a b import org.openstreetmap.josm.gui.util.GuiHelper;  
    2121import org.openstreetmap.josm.io.CertificateAmendment;
    2222import org.openstreetmap.josm.io.OsmApi;
    2323import org.openstreetmap.josm.tools.I18n;
     24import org.openstreetmap.josm.tools.Logging;
    2425
    2526/**
    2627 * Fixture to define a proper and safe environment before running tests.
    public class JOSMFixture {  
    99100        Main.platform.preStartupHook();
    100101
    101102        Main.logLevel = 3;
     103        Logging.setLogLevel(Logging.LEVEL_INFO);
    102104        Main.pref.init(false);
    103105        Main.pref.put("osm-server.url", "http://api06.dev.openstreetmap.org/api");
    104106        I18n.set(Main.pref.get("language", "en"));
  • test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java

    diff --git a/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java b/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
    index 634b625..d46b251 100644
    a b import java.io.File;  
    55import java.io.IOException;
    66import java.text.MessageFormat;
    77import java.util.TimeZone;
     8import java.util.logging.Level;
    89
    910import org.junit.rules.TemporaryFolder;
    1011import org.junit.rules.TestRule;
    import org.openstreetmap.josm.io.OsmApi;  
    1920import org.openstreetmap.josm.io.OsmApiInitializationException;
    2021import org.openstreetmap.josm.io.OsmTransferCanceledException;
    2122import org.openstreetmap.josm.tools.I18n;
     23import org.openstreetmap.josm.tools.Logging;
    2224import org.openstreetmap.josm.tools.MemoryManagerTest;
    2325import org.openstreetmap.josm.tools.date.DateUtils;
    2426
    public class JOSMTestRules implements TestRule {  
    182184        TimeZone.setDefault(DateUtils.UTC);
    183185        // Set log level to info
    184186        Main.logLevel = 3;
     187        Logging.setLogLevel(Level.INFO);
    185188
    186189        // Set up i18n
    187190        if (i18n != null) {