source: josm/trunk/src/org/openstreetmap/josm/Main.java @ 12718

Last change on this file since 12718 was 12718, checked in by Don-vip, 3 months ago

see #13036 - see #15229 - see #15182 - make Commands depends only on a DataSet, not a Layer. This removes a lot of GUI dependencies

  • Property svn:eol-style set to native
File size: 38.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.GraphicsEnvironment;
8import java.io.IOException;
9import java.lang.ref.WeakReference;
10import java.net.URL;
11import java.text.MessageFormat;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.EnumSet;
16import java.util.HashMap;
17import java.util.Iterator;
18import java.util.List;
19import java.util.Locale;
20import java.util.Map;
21import java.util.Objects;
22import java.util.Set;
23import java.util.concurrent.Callable;
24import java.util.concurrent.ExecutionException;
25import java.util.concurrent.ExecutorService;
26import java.util.concurrent.Executors;
27import java.util.concurrent.Future;
28
29import javax.swing.Action;
30
31import org.openstreetmap.josm.actions.JosmAction;
32import org.openstreetmap.josm.data.Bounds;
33import org.openstreetmap.josm.data.Preferences;
34import org.openstreetmap.josm.data.UndoRedoHandler;
35import org.openstreetmap.josm.data.cache.JCSCacheManager;
36import org.openstreetmap.josm.data.coor.CoordinateFormat;
37import org.openstreetmap.josm.data.osm.DataSet;
38import org.openstreetmap.josm.data.osm.OsmPrimitive;
39import org.openstreetmap.josm.data.projection.Projection;
40import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
41import org.openstreetmap.josm.gui.MainApplication;
42import org.openstreetmap.josm.gui.MainMenu;
43import org.openstreetmap.josm.gui.MainPanel;
44import org.openstreetmap.josm.gui.MapFrame;
45import org.openstreetmap.josm.gui.MapFrameListener;
46import org.openstreetmap.josm.gui.layer.MainLayerManager;
47import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
48import org.openstreetmap.josm.io.FileWatcher;
49import org.openstreetmap.josm.io.OnlineResource;
50import org.openstreetmap.josm.io.OsmApi;
51import org.openstreetmap.josm.tools.CheckParameterUtil;
52import org.openstreetmap.josm.tools.ImageProvider;
53import org.openstreetmap.josm.tools.JosmRuntimeException;
54import org.openstreetmap.josm.tools.Logging;
55import org.openstreetmap.josm.tools.PlatformHook;
56import org.openstreetmap.josm.tools.PlatformHookOsx;
57import org.openstreetmap.josm.tools.PlatformHookUnixoid;
58import org.openstreetmap.josm.tools.PlatformHookWindows;
59import org.openstreetmap.josm.tools.Shortcut;
60import org.openstreetmap.josm.tools.Utils;
61import org.openstreetmap.josm.tools.bugreport.BugReport;
62
63/**
64 * Abstract class holding various static global variables and methods used in large parts of JOSM application.
65 * @since 98
66 */
67public abstract class Main {
68
69    /**
70     * The JOSM website URL.
71     * @since 6897 (was public from 6143 to 6896)
72     */
73    private static final String JOSM_WEBSITE = "https://josm.openstreetmap.de";
74
75    /**
76     * The OSM website URL.
77     * @since 6897 (was public from 6453 to 6896)
78     */
79    private static final String OSM_WEBSITE = "https://www.openstreetmap.org";
80
81    /**
82     * Replies true if JOSM currently displays a map view. False, if it doesn't, i.e. if
83     * it only shows the MOTD panel.
84     * <p>
85     * You do not need this when accessing the layer manager. The layer manager will be empty if no map view is shown.
86     *
87     * @return <code>true</code> if JOSM currently displays a map view
88     * @deprecated use {@link org.openstreetmap.josm.gui.MainApplication#isDisplayingMapView()}
89     */
90    @Deprecated
91    public static boolean isDisplayingMapView() {
92        return MainApplication.isDisplayingMapView();
93    }
94
95    /**
96     * Global parent component for all dialogs and message boxes
97     */
98    public static Component parent;
99
100    /**
101     * Global application.
102     */
103    public static volatile Main main;
104
105    /**
106     * The worker thread slave. This is for executing all long and intensive
107     * calculations. The executed runnables are guaranteed to be executed separately and sequential.
108     * @deprecated use {@link MainApplication#worker} instead
109     */
110    @Deprecated
111    public static ExecutorService worker;
112
113    /**
114     * Global application preferences
115     */
116    public static final Preferences pref = new Preferences();
117
118    /**
119     * The MapFrame.
120     * <p>
121     * There should be no need to access this to access any map data. Use {@link MainApplication#getLayerManager} instead.
122     *
123     * @deprecated Use {@link MainApplication#getMap()} instead
124     * @see MainPanel
125     */
126    @Deprecated
127    public static MapFrame map;
128
129    /**
130     * The toolbar preference control to register new actions.
131     * @deprecated Use {@link MainApplication#getToolbar} instead
132     */
133    @Deprecated
134    public static volatile ToolbarPreferences toolbar;
135
136    /**
137     * The commands undo/redo handler.
138     */
139    public final UndoRedoHandler undoRedo = new UndoRedoHandler();
140
141    /**
142     * The main menu bar at top of screen.
143     * @deprecated Use {@link MainApplication#getMenu} instead
144     */
145    @Deprecated
146    public MainMenu menu;
147
148    /**
149     * The main panel.
150     * @deprecated Use {@link MainApplication#getMainPanel} instead
151     * @since 12125
152     */
153    @Deprecated
154    public MainPanel panel;
155
156    /**
157     * The file watcher service.
158     */
159    public static final FileWatcher fileWatcher = new FileWatcher();
160
161    private static final Map<String, Throwable> NETWORK_ERRORS = new HashMap<>();
162
163    private static final Set<OnlineResource> OFFLINE_RESOURCES = EnumSet.noneOf(OnlineResource.class);
164
165    /**
166     * Logging level (5 = trace, 4 = debug, 3 = info, 2 = warn, 1 = error, 0 = none).
167     * @since 6248
168     * @deprecated Use {@link Logging} class.
169     */
170    @Deprecated
171    public static int logLevel = 3;
172
173    /**
174     * Replies the first lines of last 5 error and warning messages, used for bug reports
175     * @return the first lines of last 5 error and warning messages
176     * @since 7420
177     * @deprecated Use {@link Logging#getLastErrorAndWarnings}.
178     */
179    @Deprecated
180    public static final Collection<String> getLastErrorAndWarnings() {
181        return Logging.getLastErrorAndWarnings();
182    }
183
184    /**
185     * Clears the list of last error and warning messages.
186     * @since 8959
187     * @deprecated Use {@link Logging#clearLastErrorAndWarnings}.
188     */
189    @Deprecated
190    public static void clearLastErrorAndWarnings() {
191        Logging.clearLastErrorAndWarnings();
192    }
193
194    /**
195     * Prints an error message if logging is on.
196     * @param msg The message to print.
197     * @since 6248
198     * @deprecated Use {@link Logging#error(String)}.
199     */
200    @Deprecated
201    public static void error(String msg) {
202        Logging.error(msg);
203    }
204
205    /**
206     * Prints a warning message if logging is on.
207     * @param msg The message to print.
208     * @deprecated Use {@link Logging#warn(String)}.
209     */
210    @Deprecated
211    public static void warn(String msg) {
212        Logging.warn(msg);
213    }
214
215    /**
216     * Prints an informational message if logging is on.
217     * @param msg The message to print.
218     * @deprecated Use {@link Logging#info(String)}.
219     */
220    @Deprecated
221    public static void info(String msg) {
222        Logging.info(msg);
223    }
224
225    /**
226     * Prints a debug message if logging is on.
227     * @param msg The message to print.
228     * @deprecated Use {@link Logging#debug(String)}.
229     */
230    @Deprecated
231    public static void debug(String msg) {
232        Logging.debug(msg);
233    }
234
235    /**
236     * Prints a trace message if logging is on.
237     * @param msg The message to print.
238     * @deprecated Use {@link Logging#trace(String)}.
239     */
240    @Deprecated
241    public static void trace(String msg) {
242        Logging.trace(msg);
243    }
244
245    /**
246     * Determines if debug log level is enabled.
247     * Useful to avoid costly construction of debug messages when not enabled.
248     * @return {@code true} if log level is at least debug, {@code false} otherwise
249     * @since 6852
250     * @deprecated Use {@link Logging#isDebugEnabled}.
251     */
252    @Deprecated
253    public static boolean isDebugEnabled() {
254        return Logging.isDebugEnabled();
255    }
256
257    /**
258     * Determines if trace log level is enabled.
259     * Useful to avoid costly construction of trace messages when not enabled.
260     * @return {@code true} if log level is at least trace, {@code false} otherwise
261     * @since 6852
262     * @deprecated Use {@link Logging#isTraceEnabled}.
263     */
264    @Deprecated
265    public static boolean isTraceEnabled() {
266        return Logging.isTraceEnabled();
267    }
268
269    /**
270     * Prints a formatted error message if logging is on. Calls {@link MessageFormat#format}
271     * function to format text.
272     * @param msg The formatted message to print.
273     * @param objects The objects to insert into format string.
274     * @since 6248
275     * @deprecated Use {@link Logging#error(String, Object...)}.
276     */
277    @Deprecated
278    public static void error(String msg, Object... objects) {
279        Logging.error(msg, objects);
280    }
281
282    /**
283     * Prints a formatted warning message if logging is on. Calls {@link MessageFormat#format}
284     * function to format text.
285     * @param msg The formatted message to print.
286     * @param objects The objects to insert into format string.
287     * @deprecated Use {@link Logging#warn(String, Object...)}.
288     */
289    @Deprecated
290    public static void warn(String msg, Object... objects) {
291        Logging.warn(msg, objects);
292    }
293
294    /**
295     * Prints a formatted informational message if logging is on. Calls {@link MessageFormat#format}
296     * function to format text.
297     * @param msg The formatted message to print.
298     * @param objects The objects to insert into format string.
299     * @deprecated Use {@link Logging#info(String, Object...)}.
300     */
301    @Deprecated
302    public static void info(String msg, Object... objects) {
303        Logging.info(msg, objects);
304    }
305
306    /**
307     * Prints a formatted debug message if logging is on. Calls {@link MessageFormat#format}
308     * function to format text.
309     * @param msg The formatted message to print.
310     * @param objects The objects to insert into format string.
311     * @deprecated Use {@link Logging#debug(String, Object...)}.
312     */
313    @Deprecated
314    public static void debug(String msg, Object... objects) {
315        Logging.debug(msg, objects);
316    }
317
318    /**
319     * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format}
320     * function to format text.
321     * @param msg The formatted message to print.
322     * @param objects The objects to insert into format string.
323     * @deprecated Use {@link Logging#trace(String, Object...)}.
324     */
325    @Deprecated
326    public static void trace(String msg, Object... objects) {
327        Logging.trace(msg, objects);
328    }
329
330    /**
331     * Prints an error message for the given Throwable.
332     * @param t The throwable object causing the error
333     * @since 6248
334     * @deprecated Use {@link Logging#error(Throwable)}.
335     */
336    @Deprecated
337    public static void error(Throwable t) {
338        Logging.error(t);
339    }
340
341    /**
342     * Prints a warning message for the given Throwable.
343     * @param t The throwable object causing the error
344     * @since 6248
345     * @deprecated Use {@link Logging#warn(Throwable)}.
346     */
347    @Deprecated
348    public static void warn(Throwable t) {
349        Logging.warn(t);
350    }
351
352    /**
353     * Prints a debug message for the given Throwable. Useful for exceptions usually ignored
354     * @param t The throwable object causing the error
355     * @since 10420
356     * @deprecated Use {@link Logging#debug(Throwable)}.
357     */
358    @Deprecated
359    public static void debug(Throwable t) {
360        Logging.debug(t);
361    }
362
363    /**
364     * Prints a trace message for the given Throwable. Useful for exceptions usually ignored
365     * @param t The throwable object causing the error
366     * @since 10420
367     * @deprecated Use {@link Logging#trace(Throwable)}.
368     */
369    @Deprecated
370    public static void trace(Throwable t) {
371        Logging.trace(t);
372    }
373
374    /**
375     * Prints an error message for the given Throwable.
376     * @param t The throwable object causing the error
377     * @param stackTrace {@code true}, if the stacktrace should be displayed
378     * @since 6642
379     * @deprecated Use {@link Logging#log(java.util.logging.Level, Throwable)}
380     *              or {@link Logging#logWithStackTrace(java.util.logging.Level, Throwable)}.
381     */
382    @Deprecated
383    public static void error(Throwable t, boolean stackTrace) {
384        if (stackTrace) {
385            Logging.log(Logging.LEVEL_ERROR, t);
386        } else {
387            Logging.logWithStackTrace(Logging.LEVEL_ERROR, t);
388        }
389    }
390
391    /**
392     * Prints an error message for the given Throwable.
393     * @param t The throwable object causing the error
394     * @param message additional error message
395     * @since 10420
396     * @deprecated Use {@link Logging#log(java.util.logging.Level, String, Throwable)}.
397     */
398    @Deprecated
399    public static void error(Throwable t, String message) {
400        Logging.log(Logging.LEVEL_ERROR, message, t);
401    }
402
403    /**
404     * Prints a warning message for the given Throwable.
405     * @param t The throwable object causing the error
406     * @param stackTrace {@code true}, if the stacktrace should be displayed
407     * @since 6642
408     * @deprecated Use {@link Logging#log(java.util.logging.Level, Throwable)}
409     *              or {@link Logging#logWithStackTrace(java.util.logging.Level, Throwable)}.
410     */
411    @Deprecated
412    public static void warn(Throwable t, boolean stackTrace) {
413        if (stackTrace) {
414            Logging.log(Logging.LEVEL_WARN, t);
415        } else {
416            Logging.logWithStackTrace(Logging.LEVEL_WARN, t);
417        }
418    }
419
420    /**
421     * Prints a warning message for the given Throwable.
422     * @param t The throwable object causing the error
423     * @param message additional error message
424     * @since 10420
425     * @deprecated Use {@link Logging#log(java.util.logging.Level, String, Throwable)}.
426     */
427    @Deprecated
428    public static void warn(Throwable t, String message) {
429        Logging.log(Logging.LEVEL_WARN, message, t);
430    }
431
432    /**
433     * Returns a human-readable message of error, also usable for developers.
434     * @param t The error
435     * @return The human-readable error message
436     * @since 6642
437     * @deprecated Use {@link Logging#getErrorMessage}.
438     */
439    @Deprecated
440    public static String getErrorMessage(Throwable t) {
441        if (t == null) {
442            return null;
443        } else {
444            return Logging.getErrorMessage(t);
445        }
446    }
447
448    /**
449     * Platform specific code goes in here.
450     * Plugins may replace it, however, some hooks will be called before any plugins have been loaded.
451     * So if you need to hook into those early ones, split your class and send the one with the early hooks
452     * to the JOSM team for inclusion.
453     */
454    public static volatile PlatformHook platform;
455
456    private static volatile InitStatusListener initListener;
457
458    /**
459     * Initialization task listener.
460     */
461    public interface InitStatusListener {
462
463        /**
464         * Called when an initialization task updates its status.
465         * @param event task name
466         * @return new status
467         */
468        Object updateStatus(String event);
469
470        /**
471         * Called when an initialization task completes.
472         * @param status final status
473         */
474        void finish(Object status);
475    }
476
477    /**
478     * Sets initialization task listener.
479     * @param listener initialization task listener
480     */
481    public static void setInitStatusListener(InitStatusListener listener) {
482        CheckParameterUtil.ensureParameterNotNull(listener);
483        initListener = listener;
484    }
485
486    /**
487     * Constructs new {@code Main} object.
488     * @see #initialize()
489     */
490    protected Main() {
491        setInstance(this);
492    }
493
494    private static void setInstance(Main instance) {
495        main = instance;
496    }
497
498    /**
499     * Initializes the main object. A lot of global variables are initialized here.
500     * @since 10340
501     */
502    public void initialize() {
503        // Initializes tasks that must be run before parallel tasks
504        runInitializationTasks(beforeInitializationTasks());
505
506        // Initializes tasks to be executed (in parallel) by a ExecutorService
507        try {
508            ExecutorService service = Executors.newFixedThreadPool(
509                    Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY));
510            for (Future<Void> i : service.invokeAll(parallelInitializationTasks())) {
511                i.get();
512            }
513            // asynchronous initializations to be completed eventually
514            asynchronousRunnableTasks().forEach(service::submit);
515            asynchronousCallableTasks().forEach(service::submit);
516            service.shutdown();
517        } catch (InterruptedException | ExecutionException ex) {
518            throw new JosmRuntimeException(ex);
519        }
520
521        // Initializes tasks that must be run after parallel tasks
522        runInitializationTasks(afterInitializationTasks());
523    }
524
525    private static void runInitializationTasks(List<InitializationTask> tasks) {
526        for (InitializationTask task : tasks) {
527            try {
528                task.call();
529            } catch (JosmRuntimeException e) {
530                // Can happen if the current projection needs NTV2 grid which is not available
531                // In this case we want the user be able to change his projection
532                BugReport.intercept(e).warn();
533            }
534        }
535    }
536
537    /**
538     * Returns tasks that must be run before parallel tasks.
539     * @return tasks that must be run before parallel tasks
540     * @see #afterInitializationTasks
541     * @see #parallelInitializationTasks
542     */
543    protected List<InitializationTask> beforeInitializationTasks() {
544        return Collections.emptyList();
545    }
546
547    /**
548     * Returns tasks to be executed (in parallel) by a ExecutorService.
549     * @return tasks to be executed (in parallel) by a ExecutorService
550     */
551    protected Collection<InitializationTask> parallelInitializationTasks() {
552        return Collections.emptyList();
553    }
554
555    /**
556     * Returns asynchronous callable initializations to be completed eventually
557     * @return asynchronous callable initializations to be completed eventually
558     */
559    protected List<Callable<?>> asynchronousCallableTasks() {
560        return Collections.emptyList();
561    }
562
563    /**
564     * Returns asynchronous runnable initializations to be completed eventually
565     * @return asynchronous runnable initializations to be completed eventually
566     */
567    protected List<Runnable> asynchronousRunnableTasks() {
568        return Collections.emptyList();
569    }
570
571    /**
572     * Returns tasks that must be run after parallel tasks.
573     * @return tasks that must be run after parallel tasks
574     * @see #beforeInitializationTasks
575     * @see #parallelInitializationTasks
576     */
577    protected List<InitializationTask> afterInitializationTasks() {
578        return Collections.emptyList();
579    }
580
581    protected static final class InitializationTask implements Callable<Void> {
582
583        private final String name;
584        private final Runnable task;
585
586        /**
587         * Constructs a new {@code InitializationTask}.
588         * @param name translated name to be displayed to user
589         * @param task runnable initialization task
590         */
591        public InitializationTask(String name, Runnable task) {
592            this.name = name;
593            this.task = task;
594        }
595
596        @Override
597        public Void call() {
598            Object status = null;
599            if (initListener != null) {
600                status = initListener.updateStatus(name);
601            }
602            task.run();
603            if (initListener != null) {
604                initListener.finish(status);
605            }
606            return null;
607        }
608    }
609
610    /**
611     * Returns the main layer manager that is used by the map view.
612     * @return The layer manager. The value returned will never change.
613     * @since 10279
614     * @deprecated use {@link MainApplication#getLayerManager} instead
615     */
616    @Deprecated
617    public static MainLayerManager getLayerManager() {
618        return MainApplication.getLayerManager();
619    }
620
621    /**
622     * Replies the current selected primitives, from a end-user point of view.
623     * It is not always technically the same collection of primitives than {@link DataSet#getSelected()}.
624     * @return The current selected primitives, from a end-user point of view. Can be {@code null}.
625     * @since 6546
626     */
627    public Collection<OsmPrimitive> getInProgressSelection() {
628        return Collections.emptyList();
629    }
630
631    /**
632     * Gets the active edit data set.
633     * @return That data set, <code>null</code>.
634     * @since 12691
635     */
636    public abstract DataSet getEditDataSet();
637
638    /**
639     * Sets the active data set.
640     * @param ds New edit data set, or <code>null</code>
641     * @since 12718
642     */
643    public abstract void setEditDataSet(DataSet ds);
644
645    /**
646     * Determines if the list of data sets managed by JOSM contains {@code ds}.
647     * @param ds the data set to look for
648     * @return {@code true} if the list of data sets managed by JOSM contains {@code ds}
649     * @since 12718
650     */
651    public abstract boolean containsDataSet(DataSet ds);
652
653    /**
654     * Registers a {@code JosmAction} and its shortcut.
655     * @param action action defining its own shortcut
656     * @deprecated use {@link MainApplication#registerActionShortcut(JosmAction)} instead
657     */
658    @Deprecated
659    public static void registerActionShortcut(JosmAction action) {
660        MainApplication.registerActionShortcut(action);
661    }
662
663    /**
664     * Registers an action and its shortcut.
665     * @param action action to register
666     * @param shortcut shortcut to associate to {@code action}
667     * @deprecated use {@link MainApplication#registerActionShortcut(Action, Shortcut)} instead
668     */
669    @Deprecated
670    public static void registerActionShortcut(Action action, Shortcut shortcut) {
671        MainApplication.registerActionShortcut(action, shortcut);
672    }
673
674    /**
675     * Unregisters a shortcut.
676     * @param shortcut shortcut to unregister
677     * @deprecated use {@link MainApplication#unregisterShortcut(Shortcut)} instead
678     */
679    @Deprecated
680    public static void unregisterShortcut(Shortcut shortcut) {
681        MainApplication.unregisterShortcut(shortcut);
682    }
683
684    /**
685     * Unregisters a {@code JosmAction} and its shortcut.
686     * @param action action to unregister
687     * @deprecated use {@link MainApplication#unregisterActionShortcut(JosmAction)} instead
688     */
689    @Deprecated
690    public static void unregisterActionShortcut(JosmAction action) {
691        MainApplication.unregisterActionShortcut(action);
692    }
693
694    /**
695     * Unregisters an action and its shortcut.
696     * @param action action to unregister
697     * @param shortcut shortcut to unregister
698     * @deprecated use {@link MainApplication#unregisterActionShortcut(Action, Shortcut)} instead
699     */
700    @Deprecated
701    public static void unregisterActionShortcut(Action action, Shortcut shortcut) {
702        MainApplication.unregisterActionShortcut(action, shortcut);
703    }
704
705    /**
706     * Replies the registered action for the given shortcut
707     * @param shortcut The shortcut to look for
708     * @return the registered action for the given shortcut
709     * @deprecated use {@link MainApplication#getRegisteredActionShortcut(Shortcut)} instead
710     * @since 5696
711     */
712    @Deprecated
713    public static Action getRegisteredActionShortcut(Shortcut shortcut) {
714        return MainApplication.getRegisteredActionShortcut(shortcut);
715    }
716
717    ///////////////////////////////////////////////////////////////////////////
718    //  Implementation part
719    ///////////////////////////////////////////////////////////////////////////
720
721    /**
722     * Should be called before the main constructor to setup some parameter stuff
723     */
724    public static void preConstructorInit() {
725        // init default coordinate format
726        try {
727            CoordinateFormat.setCoordinateFormat(CoordinateFormat.valueOf(Main.pref.get("coordinates")));
728        } catch (IllegalArgumentException iae) {
729            Logging.trace(iae);
730            CoordinateFormat.setCoordinateFormat(CoordinateFormat.DECIMAL_DEGREES);
731        }
732    }
733
734    /**
735     * Closes JOSM and optionally terminates the Java Virtual Machine (JVM).
736     * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code.
737     * @param exitCode The return code
738     * @return {@code true}
739     * @since 12636
740     */
741    public static boolean exitJosm(boolean exit, int exitCode) {
742        if (Main.main != null) {
743            Main.main.shutdown();
744        }
745
746        if (exit) {
747            System.exit(exitCode);
748        }
749        return true;
750    }
751
752    /**
753     * Shutdown JOSM.
754     */
755    protected void shutdown() {
756        if (!GraphicsEnvironment.isHeadless()) {
757            ImageProvider.shutdown(false);
758            JCSCacheManager.shutdown();
759        }
760        try {
761            pref.saveDefaults();
762        } catch (IOException ex) {
763            Logging.log(Logging.LEVEL_WARN, tr("Failed to save default preferences."), ex);
764        }
765        if (!GraphicsEnvironment.isHeadless()) {
766            ImageProvider.shutdown(true);
767        }
768    }
769
770    /**
771     * Identifies the current operating system family and initializes the platform hook accordingly.
772     * @since 1849
773     */
774    public static void determinePlatformHook() {
775        String os = System.getProperty("os.name");
776        if (os == null) {
777            Logging.warn("Your operating system has no name, so I'm guessing its some kind of *nix.");
778            platform = new PlatformHookUnixoid();
779        } else if (os.toLowerCase(Locale.ENGLISH).startsWith("windows")) {
780            platform = new PlatformHookWindows();
781        } else if ("Linux".equals(os) || "Solaris".equals(os) ||
782                "SunOS".equals(os) || "AIX".equals(os) ||
783                "FreeBSD".equals(os) || "NetBSD".equals(os) || "OpenBSD".equals(os)) {
784            platform = new PlatformHookUnixoid();
785        } else if (os.toLowerCase(Locale.ENGLISH).startsWith("mac os x")) {
786            platform = new PlatformHookOsx();
787        } else {
788            Logging.warn("I don't know your operating system '"+os+"', so I'm guessing its some kind of *nix.");
789            platform = new PlatformHookUnixoid();
790        }
791    }
792
793    /* ----------------------------------------------------------------------------------------- */
794    /* projection handling  - Main is a registry for a single, global projection instance        */
795    /*                                                                                           */
796    /* TODO: For historical reasons the registry is implemented by Main. An alternative approach */
797    /* would be a singleton org.openstreetmap.josm.data.projection.ProjectionRegistry class.     */
798    /* ----------------------------------------------------------------------------------------- */
799    /**
800     * The projection method used.
801     * Use {@link #getProjection()} and {@link #setProjection(Projection)} for access.
802     * Use {@link #setProjection(Projection)} in order to trigger a projection change event.
803     */
804    private static volatile Projection proj;
805
806    /**
807     * Replies the current projection.
808     *
809     * @return the currently active projection
810     */
811    public static Projection getProjection() {
812        return proj;
813    }
814
815    /**
816     * Sets the current projection
817     *
818     * @param p the projection
819     */
820    public static void setProjection(Projection p) {
821        CheckParameterUtil.ensureParameterNotNull(p);
822        Projection oldValue = proj;
823        Bounds b = main != null ? main.getRealBounds() : null;
824        proj = p;
825        fireProjectionChanged(oldValue, proj, b);
826    }
827
828    /**
829     * Returns the bounds for the current projection. Used for projection events.
830     * @return the bounds for the current projection
831     * @see #restoreOldBounds
832     */
833    protected Bounds getRealBounds() {
834        // To be overriden
835        return null;
836    }
837
838    /**
839     * Restore clean state corresponding to old bounds after a projection change event.
840     * @param oldBounds bounds previously returned by {@link #getRealBounds}, before the change of projection
841     * @see #getRealBounds
842     */
843    protected void restoreOldBounds(Bounds oldBounds) {
844        // To be overriden
845    }
846
847    /*
848     * Keep WeakReferences to the listeners. This relieves clients from the burden of
849     * explicitly removing the listeners and allows us to transparently register every
850     * created dataset as projection change listener.
851     */
852    private static final List<WeakReference<ProjectionChangeListener>> listeners = new ArrayList<>();
853
854    private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
855        if ((newValue == null ^ oldValue == null)
856                || (newValue != null && oldValue != null && !Objects.equals(newValue.toCode(), oldValue.toCode()))) {
857            synchronized (Main.class) {
858                Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator();
859                while (it.hasNext()) {
860                    WeakReference<ProjectionChangeListener> wr = it.next();
861                    ProjectionChangeListener listener = wr.get();
862                    if (listener == null) {
863                        it.remove();
864                        continue;
865                    }
866                    listener.projectionChanged(oldValue, newValue);
867                }
868            }
869            if (newValue != null && oldBounds != null && main != null) {
870                main.restoreOldBounds(oldBounds);
871            }
872            /* TODO - remove layers with fixed projection */
873        }
874    }
875
876    /**
877     * Register a projection change listener.
878     * The listener is registered to be weak, so keep a reference of it if you want it to be preserved.
879     *
880     * @param listener the listener. Ignored if <code>null</code>.
881     */
882    public static void addProjectionChangeListener(ProjectionChangeListener listener) {
883        if (listener == null) return;
884        synchronized (Main.class) {
885            for (WeakReference<ProjectionChangeListener> wr : listeners) {
886                // already registered ? => abort
887                if (wr.get() == listener) return;
888            }
889            listeners.add(new WeakReference<>(listener));
890        }
891    }
892
893    /**
894     * Removes a projection change listener.
895     *
896     * @param listener the listener. Ignored if <code>null</code>.
897     */
898    public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
899        if (listener == null) return;
900        synchronized (Main.class) {
901            // remove the listener - and any other listener which got garbage
902            // collected in the meantime
903            listeners.removeIf(wr -> wr.get() == null || wr.get() == listener);
904        }
905    }
906
907    /**
908     * Listener for window switch events.
909     *
910     * These are events, when the user activates a window of another application
911     * or comes back to JOSM. Window switches from one JOSM window to another
912     * are not reported.
913     */
914    public interface WindowSwitchListener {
915        /**
916         * Called when the user activates a window of another application.
917         */
918        void toOtherApplication();
919
920        /**
921         * Called when the user comes from a window of another application back to JOSM.
922         */
923        void fromOtherApplication();
924    }
925
926    /**
927     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
928     * <p>
929     * It will fire an initial mapFrameInitialized event when the MapFrame is present.
930     * Otherwise will only fire when the MapFrame is created or destroyed.
931     * @param listener The MapFrameListener
932     * @return {@code true} if the listeners collection changed as a result of the call
933     * @see #addMapFrameListener
934     * @deprecated use {@link MainApplication#addAndFireMapFrameListener} instead
935     * @since 11904
936     */
937    @Deprecated
938    public static boolean addAndFireMapFrameListener(MapFrameListener listener) {
939        return MainApplication.addAndFireMapFrameListener(listener);
940    }
941
942    /**
943     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
944     * @param listener The MapFrameListener
945     * @return {@code true} if the listeners collection changed as a result of the call
946     * @see #addAndFireMapFrameListener
947     * @deprecated use {@link MainApplication#addMapFrameListener} instead
948     * @since 5957
949     */
950    @Deprecated
951    public static boolean addMapFrameListener(MapFrameListener listener) {
952        return MainApplication.addMapFrameListener(listener);
953    }
954
955    /**
956     * Unregisters the given {@code MapFrameListener} from MapFrame changes
957     * @param listener The MapFrameListener
958     * @return {@code true} if the listeners collection changed as a result of the call
959     * @deprecated use {@link MainApplication#removeMapFrameListener} instead
960     * @since 5957
961     */
962    @Deprecated
963    public static boolean removeMapFrameListener(MapFrameListener listener) {
964        return MainApplication.removeMapFrameListener(listener);
965    }
966
967    /**
968     * Adds a new network error that occur to give a hint about broken Internet connection.
969     * Do not use this method for errors known for sure thrown because of a bad proxy configuration.
970     *
971     * @param url The accessed URL that caused the error
972     * @param t The network error
973     * @return The previous error associated to the given resource, if any. Can be {@code null}
974     * @since 6642
975     */
976    public static Throwable addNetworkError(URL url, Throwable t) {
977        if (url != null && t != null) {
978            Throwable old = addNetworkError(url.toExternalForm(), t);
979            if (old != null) {
980                Logging.warn("Already here "+old);
981            }
982            return old;
983        }
984        return null;
985    }
986
987    /**
988     * Adds a new network error that occur to give a hint about broken Internet connection.
989     * Do not use this method for errors known for sure thrown because of a bad proxy configuration.
990     *
991     * @param url The accessed URL that caused the error
992     * @param t The network error
993     * @return The previous error associated to the given resource, if any. Can be {@code null}
994     * @since 6642
995     */
996    public static Throwable addNetworkError(String url, Throwable t) {
997        if (url != null && t != null) {
998            return NETWORK_ERRORS.put(url, t);
999        }
1000        return null;
1001    }
1002
1003    /**
1004     * Returns the network errors that occured until now.
1005     * @return the network errors that occured until now, indexed by URL
1006     * @since 6639
1007     */
1008    public static Map<String, Throwable> getNetworkErrors() {
1009        return new HashMap<>(NETWORK_ERRORS);
1010    }
1011
1012    /**
1013     * Clears the network errors cache.
1014     * @since 12011
1015     */
1016    public static void clearNetworkErrors() {
1017        NETWORK_ERRORS.clear();
1018    }
1019
1020    /**
1021     * Returns the JOSM website URL.
1022     * @return the josm website URL
1023     * @since 6897
1024     */
1025    public static String getJOSMWebsite() {
1026        if (Main.pref != null)
1027            return Main.pref.get("josm.url", JOSM_WEBSITE);
1028        return JOSM_WEBSITE;
1029    }
1030
1031    /**
1032     * Returns the JOSM XML URL.
1033     * @return the josm XML URL
1034     * @since 6897
1035     */
1036    public static String getXMLBase() {
1037        // Always return HTTP (issues reported with HTTPS)
1038        return "http://josm.openstreetmap.de";
1039    }
1040
1041    /**
1042     * Returns the OSM website URL.
1043     * @return the OSM website URL
1044     * @since 6897
1045     */
1046    public static String getOSMWebsite() {
1047        if (Main.pref != null)
1048            return Main.pref.get("osm.url", OSM_WEBSITE);
1049        return OSM_WEBSITE;
1050    }
1051
1052    /**
1053     * Returns the OSM website URL depending on the selected {@link OsmApi}.
1054     * @return the OSM website URL depending on the selected {@link OsmApi}
1055     */
1056    private static String getOSMWebsiteDependingOnSelectedApi() {
1057        final String api = OsmApi.getOsmApi().getServerUrl();
1058        if (OsmApi.DEFAULT_API_URL.equals(api)) {
1059            return getOSMWebsite();
1060        } else {
1061            return api.replaceAll("/api$", "");
1062        }
1063    }
1064
1065    /**
1066     * Replies the base URL for browsing information about a primitive.
1067     * @return the base URL, i.e. https://www.openstreetmap.org
1068     * @since 7678
1069     */
1070    public static String getBaseBrowseUrl() {
1071        if (Main.pref != null)
1072            return Main.pref.get("osm-browse.url", getOSMWebsiteDependingOnSelectedApi());
1073        return getOSMWebsiteDependingOnSelectedApi();
1074    }
1075
1076    /**
1077     * Replies the base URL for browsing information about a user.
1078     * @return the base URL, i.e. https://www.openstreetmap.org/user
1079     * @since 7678
1080     */
1081    public static String getBaseUserUrl() {
1082        if (Main.pref != null)
1083            return Main.pref.get("osm-user.url", getOSMWebsiteDependingOnSelectedApi() + "/user");
1084        return getOSMWebsiteDependingOnSelectedApi() + "/user";
1085    }
1086
1087    /**
1088     * Determines if we are currently running on OSX.
1089     * @return {@code true} if we are currently running on OSX
1090     * @since 6957
1091     */
1092    public static boolean isPlatformOsx() {
1093        return Main.platform instanceof PlatformHookOsx;
1094    }
1095
1096    /**
1097     * Determines if we are currently running on Windows.
1098     * @return {@code true} if we are currently running on Windows
1099     * @since 7335
1100     */
1101    public static boolean isPlatformWindows() {
1102        return Main.platform instanceof PlatformHookWindows;
1103    }
1104
1105    /**
1106     * Determines if the given online resource is currently offline.
1107     * @param r the online resource
1108     * @return {@code true} if {@code r} is offline and should not be accessed
1109     * @since 7434
1110     */
1111    public static boolean isOffline(OnlineResource r) {
1112        return OFFLINE_RESOURCES.contains(r) || OFFLINE_RESOURCES.contains(OnlineResource.ALL);
1113    }
1114
1115    /**
1116     * Sets the given online resource to offline state.
1117     * @param r the online resource
1118     * @return {@code true} if {@code r} was not already offline
1119     * @since 7434
1120     */
1121    public static boolean setOffline(OnlineResource r) {
1122        return OFFLINE_RESOURCES.add(r);
1123    }
1124
1125    /**
1126     * Sets the given online resource to online state.
1127     * @param r the online resource
1128     * @return {@code true} if {@code r} was offline
1129     * @since 8506
1130     */
1131    public static boolean setOnline(OnlineResource r) {
1132        return OFFLINE_RESOURCES.remove(r);
1133    }
1134
1135    /**
1136     * Replies the set of online resources currently offline.
1137     * @return the set of online resources currently offline
1138     * @since 7434
1139     */
1140    public static Set<OnlineResource> getOfflineResources() {
1141        return EnumSet.copyOf(OFFLINE_RESOURCES);
1142    }
1143}
Note: See TracBrowser for help on using the repository browser.