001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.awt.Dimension; 008import java.io.File; 009import java.io.IOException; 010import java.io.InputStream; 011import java.net.Authenticator; 012import java.net.Inet6Address; 013import java.net.InetAddress; 014import java.net.InetSocketAddress; 015import java.net.ProxySelector; 016import java.net.Socket; 017import java.net.URL; 018import java.security.AllPermission; 019import java.security.CodeSource; 020import java.security.GeneralSecurityException; 021import java.security.KeyStoreException; 022import java.security.NoSuchAlgorithmException; 023import java.security.PermissionCollection; 024import java.security.Permissions; 025import java.security.Policy; 026import java.security.cert.CertificateException; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.List; 030import java.util.Locale; 031import java.util.Optional; 032import java.util.Set; 033import java.util.TreeSet; 034import java.util.logging.Level; 035import java.util.stream.Collectors; 036import java.util.stream.Stream; 037 038import javax.swing.JOptionPane; 039import javax.swing.RepaintManager; 040import javax.swing.SwingUtilities; 041 042import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager; 043import org.openstreetmap.josm.Main; 044import org.openstreetmap.josm.actions.PreferencesAction; 045import org.openstreetmap.josm.actions.RestartAction; 046import org.openstreetmap.josm.data.AutosaveTask; 047import org.openstreetmap.josm.data.CustomConfigurator; 048import org.openstreetmap.josm.data.Version; 049import org.openstreetmap.josm.gui.ProgramArguments.Option; 050import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor; 051import org.openstreetmap.josm.gui.download.DownloadDialog; 052import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 053import org.openstreetmap.josm.gui.preferences.server.ProxyPreference; 054import org.openstreetmap.josm.gui.util.GuiHelper; 055import org.openstreetmap.josm.io.CertificateAmendment; 056import org.openstreetmap.josm.io.DefaultProxySelector; 057import org.openstreetmap.josm.io.MessageNotifier; 058import org.openstreetmap.josm.io.OnlineResource; 059import org.openstreetmap.josm.io.auth.CredentialsManager; 060import org.openstreetmap.josm.io.auth.DefaultAuthenticator; 061import org.openstreetmap.josm.io.protocols.data.Handler; 062import org.openstreetmap.josm.io.remotecontrol.RemoteControl; 063import org.openstreetmap.josm.plugins.PluginHandler; 064import org.openstreetmap.josm.plugins.PluginInformation; 065import org.openstreetmap.josm.tools.FontsManager; 066import org.openstreetmap.josm.tools.HttpClient; 067import org.openstreetmap.josm.tools.I18n; 068import org.openstreetmap.josm.tools.Logging; 069import org.openstreetmap.josm.tools.OsmUrlToBounds; 070import org.openstreetmap.josm.tools.PlatformHookWindows; 071import org.openstreetmap.josm.tools.Utils; 072import org.openstreetmap.josm.tools.WindowGeometry; 073import org.openstreetmap.josm.tools.bugreport.BugReport; 074import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; 075 076/** 077 * Main window class application. 078 * 079 * @author imi 080 */ 081public class MainApplication extends Main { 082 083 private MainFrame mainFrame; 084 085 /** 086 * Constructs a new {@code MainApplication} without a window. 087 */ 088 public MainApplication() { 089 // Allow subclassing (see JOSM.java) 090 this(null); 091 } 092 093 /** 094 * Constructs a main frame, ready sized and operating. Does not display the frame. 095 * @param mainFrame The main JFrame of the application 096 * @since 10340 097 */ 098 public MainApplication(MainFrame mainFrame) { 099 this.mainFrame = mainFrame; 100 } 101 102 @Override 103 protected void initializeMainWindow() { 104 mainPanel.reAddListeners(); 105 if (mainFrame != null) { 106 mainFrame.initialize(); 107 108 menu = mainFrame.getMenu(); 109 } else { 110 // required for running some tests. 111 menu = new MainMenu(); 112 } 113 } 114 115 @Override 116 protected void shutdown() { 117 if (mainFrame != null) { 118 mainFrame.storeState(); 119 } 120 super.shutdown(); 121 } 122 123 /** 124 * Displays help on the console 125 * @since 2748 126 */ 127 public static void showHelp() { 128 // TODO: put in a platformHook for system that have no console by default 129 System.out.println(getHelp()); 130 } 131 132 static String getHelp() { 133 return tr("Java OpenStreetMap Editor")+" [" 134 +Version.getInstance().getAgentString()+"]\n\n"+ 135 tr("usage")+":\n"+ 136 "\tjava -jar josm.jar <options>...\n\n"+ 137 tr("options")+":\n"+ 138 "\t--help|-h "+tr("Show this help")+'\n'+ 139 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+'\n'+ 140 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+'\n'+ 141 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+'\n'+ 142 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+'\n'+ 143 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+'\n'+ 144 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+'\n'+ 145 "\t--selection=<searchstring> "+tr("Select with the given search")+'\n'+ 146 "\t--[no-]maximize "+tr("Launch in maximized mode")+'\n'+ 147 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+ 148 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+ 149 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+ 150 "\t--language=<language> "+tr("Set the language")+"\n\n"+ 151 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+ 152 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+ 153 "\t--skip-plugins "+tr("Skip loading plugins")+"\n\n"+ 154 "\t--offline=<osm_api|josm_website|all> "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+ 155 tr("options provided as Java system properties")+":\n"+ 156 align("\t-Djosm.dir.name=JOSM") + tr("Change the JOSM directory name") + "\n\n" + 157 align("\t-Djosm.pref=" + tr("/PATH/TO/JOSM/PREF ")) + tr("Set the preferences directory") + "\n" + 158 align("\t") + tr("Default: {0}", platform.getDefaultPrefDirectory()) + "\n\n" + 159 align("\t-Djosm.userdata=" + tr("/PATH/TO/JOSM/USERDATA")) + tr("Set the user data directory") + "\n" + 160 align("\t") + tr("Default: {0}", platform.getDefaultUserDataDirectory()) + "\n\n" + 161 align("\t-Djosm.cache=" + tr("/PATH/TO/JOSM/CACHE ")) + tr("Set the cache directory") + "\n" + 162 align("\t") + tr("Default: {0}", platform.getDefaultCacheDirectory()) + "\n\n" + 163 align("\t-Djosm.home=" + tr("/PATH/TO/JOSM/HOMEDIR ")) + 164 tr("Set the preferences+data+cache directory (cache directory will be josm.home/cache)")+"\n\n"+ 165 tr("-Djosm.home has lower precedence, i.e. the specific setting overrides the general one")+"\n\n"+ 166 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" + 167 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+ 168 "\t-Xmx...m\n\n"+ 169 tr("examples")+":\n"+ 170 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+ 171 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+'\n'+ 172 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+ 173 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+ 174 "\tjava -Djosm.pref=$XDG_CONFIG_HOME -Djosm.userdata=$XDG_DATA_HOME -Djosm.cache=$XDG_CACHE_HOME -jar josm.jar\n"+ 175 "\tjava -Djosm.dir.name=josm_dev -jar josm.jar\n"+ 176 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+ 177 "\tjava -Xmx1024m -jar josm.jar\n\n"+ 178 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+'\n'+ 179 tr("Make sure you load some data if you use --selection.")+'\n'; 180 } 181 182 private static String align(String str) { 183 return str + Stream.generate(() -> " ").limit(Math.max(0, 43 - str.length())).collect(Collectors.joining("")); 184 } 185 186 /** 187 * Main application Startup 188 * @param argArray Command-line arguments 189 */ 190 public static void main(final String[] argArray) { 191 I18n.init(); 192 193 // construct argument table 194 ProgramArguments args = null; 195 try { 196 args = new ProgramArguments(argArray); 197 } catch (IllegalArgumentException e) { 198 System.err.println(e.getMessage()); 199 System.exit(1); 200 return; 201 } 202 203 Level logLevel = args.getLogLevel(); 204 Logging.setLogLevel(logLevel); 205 if (!args.showVersion() && !args.showHelp()) { 206 Main.info(tr("Log level is at {0} ({1}, {2})", logLevel.getLocalizedName(), logLevel.getName(), logLevel.intValue())); 207 } 208 209 Optional<String> language = args.getSingle(Option.LANGUAGE); 210 I18n.set(language.orElse(null)); 211 212 Policy.setPolicy(new Policy() { 213 // Permissions for plug-ins loaded when josm is started via webstart 214 private PermissionCollection pc; 215 216 { 217 pc = new Permissions(); 218 pc.add(new AllPermission()); 219 } 220 221 @Override 222 public PermissionCollection getPermissions(CodeSource codesource) { 223 return pc; 224 } 225 }); 226 227 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler()); 228 229 // initialize the platform hook, and 230 Main.determinePlatformHook(); 231 // call the really early hook before we do anything else 232 Main.platform.preStartupHook(); 233 234 if (args.showVersion()) { 235 System.out.println(Version.getInstance().getAgentString()); 236 return; 237 } else if (args.showHelp()) { 238 showHelp(); 239 return; 240 } 241 242 Main.COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray)); 243 244 boolean skipLoadingPlugins = args.hasOption(Option.SKIP_PLUGINS); 245 if (skipLoadingPlugins) { 246 Main.info(tr("Plugin loading skipped")); 247 } 248 249 if (Logging.isLoggingEnabled(Logging.LEVEL_TRACE)) { 250 // Enable debug in OAuth signpost via system preference, but only at trace level 251 Utils.updateSystemProperty("debug", "true"); 252 Main.info(tr("Enabled detailed debug level (trace)")); 253 } 254 255 Main.pref.init(args.hasOption(Option.RESET_PREFERENCES)); 256 257 args.getPreferencesToSet().forEach(Main.pref::put); 258 259 if (!language.isPresent()) { 260 I18n.set(Main.pref.get("language", null)); 261 } 262 Main.pref.updateSystemProperties(); 263 264 checkIPv6(); 265 266 processOffline(args); 267 268 Main.platform.afterPrefStartupHook(); 269 270 FontsManager.initialize(); 271 272 I18n.setupLanguageFonts(); 273 274 Handler.install(); 275 276 WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry", 277 args.getSingle(Option.GEOMETRY).orElse(null), 278 !args.hasOption(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false)); 279 final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry); 280 Main.parent = mainFrame; 281 282 if (args.hasOption(Option.LOAD_PREFERENCES)) { 283 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 284 for (String i : args.get(Option.LOAD_PREFERENCES)) { 285 info("Reading preferences from " + i); 286 try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) { 287 config.openAndReadXML(is); 288 } catch (IOException ex) { 289 throw BugReport.intercept(ex).put("file", i); 290 } 291 } 292 } 293 294 try { 295 CertificateAmendment.addMissingCertificates(); 296 } catch (IOException | GeneralSecurityException ex) { 297 Main.warn(ex); 298 Main.warn(getErrorMessage(Utils.getRootCause(ex))); 299 } 300 Authenticator.setDefault(DefaultAuthenticator.getInstance()); 301 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault()); 302 ProxySelector.setDefault(proxySelector); 303 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance()); 304 305 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(SplashScreen::new); 306 final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor(); 307 monitor.beginTask(tr("Initializing")); 308 GuiHelper.runInEDT(() -> splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true))); 309 Main.setInitStatusListener(new InitStatusListener() { 310 311 @Override 312 public Object updateStatus(String event) { 313 monitor.beginTask(event); 314 return event; 315 } 316 317 @Override 318 public void finish(Object status) { 319 if (status instanceof String) { 320 monitor.finishTask((String) status); 321 } 322 } 323 }); 324 325 Collection<PluginInformation> pluginsToLoad = null; 326 327 if (!skipLoadingPlugins) { 328 pluginsToLoad = updateAndLoadEarlyPlugins(splash, monitor); 329 } 330 331 monitor.indeterminateSubTask(tr("Setting defaults")); 332 preConstructorInit(); 333 334 monitor.indeterminateSubTask(tr("Creating main GUI")); 335 final Main main = new MainApplication(mainFrame); 336 main.initialize(); 337 338 if (!skipLoadingPlugins) { 339 loadLatePlugins(splash, monitor, pluginsToLoad); 340 } 341 342 // Wait for splash disappearance (fix #9714) 343 GuiHelper.runInEDTAndWait(() -> { 344 splash.setVisible(false); 345 splash.dispose(); 346 mainFrame.setVisible(true); 347 }); 348 349 Main.MasterWindowListener.setup(); 350 351 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 352 if ((!args.hasOption(Option.NO_MAXIMIZE) && maximized) || args.hasOption(Option.MAXIMIZE)) { 353 mainFrame.setMaximized(true); 354 } 355 if (main.menu.fullscreenToggleAction != null) { 356 main.menu.fullscreenToggleAction.initial(); 357 } 358 359 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector)); 360 361 if (Main.isPlatformWindows()) { 362 try { 363 // Check for insecure certificates to remove. 364 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) 365 // neither startupHook (need to be called before remote control) 366 PlatformHookWindows.removeInsecureCertificates(); 367 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) { 368 error(e); 369 } 370 } 371 372 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 373 RemoteControl.start(); 374 } 375 376 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) { 377 MessageNotifier.start(); 378 } 379 380 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) { 381 // Repaint manager is registered so late for a reason - there is lots of violation during startup process 382 // but they don't seem to break anything and are difficult to fix 383 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console"); 384 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager()); 385 } 386 } 387 388 static Collection<PluginInformation> updateAndLoadEarlyPlugins(SplashScreen splash, SplashProgressMonitor monitor) { 389 Collection<PluginInformation> pluginsToLoad; 390 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false)); 391 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) { 392 monitor.subTask(tr("Updating plugins")); 393 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false); 394 } 395 396 monitor.indeterminateSubTask(tr("Installing updated plugins")); 397 PluginHandler.installDownloadedPlugins(true); 398 399 monitor.indeterminateSubTask(tr("Loading early plugins")); 400 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 401 return pluginsToLoad; 402 } 403 404 static void loadLatePlugins(SplashScreen splash, SplashProgressMonitor monitor, Collection<PluginInformation> pluginsToLoad) { 405 monitor.indeterminateSubTask(tr("Loading plugins")); 406 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 407 toolbar.refreshToolbarControl(); 408 } 409 410 private static void processOffline(ProgramArguments args) { 411 for (String offlineNames : args.get(Option.OFFLINE)) { 412 for (String s : offlineNames.split(",")) { 413 try { 414 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 415 } catch (IllegalArgumentException e) { 416 Main.error(e, tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.", 417 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values()))); 418 System.exit(1); 419 return; 420 } 421 } 422 } 423 Set<OnlineResource> offline = Main.getOfflineResources(); 424 if (!offline.isEmpty()) { 425 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 426 "JOSM is running in offline mode. These resources will not be available: {0}", 427 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 428 } 429 } 430 431 /** 432 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation, 433 * disabling or enabling IPV6 may only be done with next start. 434 */ 435 private static void checkIPv6() { 436 if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) { 437 new Thread((Runnable) () -> { /* this may take some time (DNS, Connect) */ 438 boolean hasv6 = false; 439 boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false); 440 try { 441 /* Use the check result from last run of the software, as after the test, value 442 changes have no effect anymore */ 443 if (wasv6) { 444 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 445 } 446 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) { 447 if (a instanceof Inet6Address) { 448 if (a.isReachable(1000)) { 449 /* be sure it REALLY works */ 450 Socket s = new Socket(); 451 s.connect(new InetSocketAddress(a, 80), 1000); 452 s.close(); 453 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 454 if (!wasv6) { 455 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart.")); 456 } else { 457 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4.")); 458 } 459 hasv6 = true; 460 } 461 break; /* we're done */ 462 } 463 } 464 } catch (IOException | SecurityException e) { 465 if (Main.isDebugEnabled()) { 466 Main.debug("Exception while checking IPv6 connectivity: "+e); 467 } 468 Main.trace(e); 469 } 470 if (wasv6 && !hasv6) { 471 Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart.")); 472 Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart! 473 new RestartAction().actionPerformed(null); 474 } 475 Main.pref.put("validated.ipv6", hasv6); 476 }, "IPv6-checker").start(); 477 } 478 } 479 480 private static class GuiFinalizationWorker implements Runnable { 481 482 private final ProgramArguments args; 483 private final DefaultProxySelector proxySelector; 484 485 GuiFinalizationWorker(ProgramArguments args, DefaultProxySelector proxySelector) { 486 this.args = args; 487 this.proxySelector = proxySelector; 488 } 489 490 @Override 491 public void run() { 492 493 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly 494 if (!handleProxyErrors()) { 495 handleNetworkErrors(); 496 } 497 498 // Restore autosave layers after crash and start autosave thread 499 handleAutosave(); 500 501 // Handle command line instructions 502 postConstructorProcessCmdLine(args); 503 504 // Show download dialog if autostart is enabled 505 DownloadDialog.autostartIfNeeded(); 506 } 507 508 private static void handleAutosave() { 509 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) { 510 AutosaveTask autosaveTask = new AutosaveTask(); 511 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles(); 512 if (!unsavedLayerFiles.isEmpty()) { 513 ExtendedDialog dialog = new ExtendedDialog( 514 Main.parent, 515 tr("Unsaved osm data"), 516 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")} 517 ); 518 dialog.setContent( 519 trn("JOSM found {0} unsaved osm data layer. ", 520 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) + 521 tr("It looks like JOSM crashed last time. Would you like to restore the data?")); 522 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"}); 523 int selection = dialog.showDialog().getValue(); 524 if (selection == 1) { 525 autosaveTask.recoverUnsavedLayers(); 526 } else if (selection == 3) { 527 autosaveTask.discardUnsavedLayers(); 528 } 529 } 530 autosaveTask.schedule(); 531 } 532 } 533 534 private static boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) { 535 if (hasErrors) { 536 ExtendedDialog ed = new ExtendedDialog( 537 Main.parent, title, 538 new String[]{tr("Change proxy settings"), tr("Cancel")}); 539 ed.setButtonIcons(new String[]{"dialogs/settings", "cancel"}).setCancelButton(2); 540 ed.setMinimumSize(new Dimension(460, 260)); 541 ed.setIcon(JOptionPane.WARNING_MESSAGE); 542 ed.setContent(message); 543 544 if (ed.showDialog().getValue() == 1) { 545 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run(); 546 } 547 } 548 return hasErrors; 549 } 550 551 private boolean handleProxyErrors() { 552 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"), 553 tr("JOSM tried to access the following resources:<br>" + 554 "{0}" + 555 "but <b>failed</b> to do so, because of the following proxy errors:<br>" + 556 "{1}" + 557 "Would you like to change your proxy settings now?", 558 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()), 559 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages()) 560 )); 561 } 562 563 private static boolean handleNetworkErrors() { 564 boolean condition = !NETWORK_ERRORS.isEmpty(); 565 if (condition) { 566 Set<String> errors = new TreeSet<>(); 567 for (Throwable t : NETWORK_ERRORS.values()) { 568 errors.add(t.toString()); 569 } 570 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"), 571 tr("JOSM tried to access the following resources:<br>" + 572 "{0}" + 573 "but <b>failed</b> to do so, because of the following network errors:<br>" + 574 "{1}" + 575 "It may be due to a missing proxy configuration.<br>" + 576 "Would you like to change your proxy settings now?", 577 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()), 578 Utils.joinAsHtmlUnorderedList(errors) 579 )); 580 } 581 return false; 582 } 583 } 584}