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

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

see #15182 - fix #15186 - fix #15192 - fix initialization of Main.worker (regression)

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