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

Last change on this file since 12644 was 12644, checked in by Don-vip, 7 years ago

see #15182 - remove dependence of I18n on GUI

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