Ticket #13318: patch-fix-13318-4.patch
File patch-fix-13318-4.patch, 71.6 KB (added by , 9 years ago) |
---|
-
src/org/openstreetmap/josm/Main.java
diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java index 01d0378..b19aea3 100644
a b import java.util.concurrent.ExecutionException; 34 34 import java.util.concurrent.ExecutorService; 35 35 import java.util.concurrent.Executors; 36 36 import java.util.concurrent.Future; 37 import java.util.logging.Handler;38 import java.util.logging.Level;39 import java.util.logging.LogRecord;40 import java.util.logging.Logger;41 37 42 38 import javax.swing.Action; 43 39 import javax.swing.InputMap; … … import org.openstreetmap.josm.data.projection.Projection; 73 69 import org.openstreetmap.josm.data.projection.ProjectionChangeListener; 74 70 import org.openstreetmap.josm.data.validation.OsmValidator; 75 71 import org.openstreetmap.josm.gui.GettingStarted; 76 import org.openstreetmap.josm.gui.MainApplication.Option;77 72 import org.openstreetmap.josm.gui.MainFrame; 78 73 import org.openstreetmap.josm.gui.MainMenu; 79 74 import org.openstreetmap.josm.gui.MainPanel; 80 75 import org.openstreetmap.josm.gui.MapFrame; 81 76 import org.openstreetmap.josm.gui.MapFrameListener; 77 import org.openstreetmap.josm.gui.ProgramArguments; 78 import org.openstreetmap.josm.gui.ProgramArguments.Option; 82 79 import org.openstreetmap.josm.gui.io.SaveLayersDialog; 83 80 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; 84 81 import org.openstreetmap.josm.gui.layer.Layer; … … import org.openstreetmap.josm.plugins.PluginHandler; 103 100 import org.openstreetmap.josm.tools.CheckParameterUtil; 104 101 import org.openstreetmap.josm.tools.I18n; 105 102 import org.openstreetmap.josm.tools.ImageProvider; 103 import org.openstreetmap.josm.tools.Logging; 106 104 import org.openstreetmap.josm.tools.OpenBrowser; 107 105 import org.openstreetmap.josm.tools.OsmUrlToBounds; 108 106 import org.openstreetmap.josm.tools.PlatformHook; … … public abstract class Main { 218 216 219 217 protected static final Map<String, Throwable> NETWORK_ERRORS = new HashMap<>(); 220 218 221 // First lines of last 5 error and warning messages, used for bug reports222 private static final List<String> ERRORS_AND_WARNINGS = Collections.<String>synchronizedList(new ArrayList<String>());223 224 219 private static final Set<OnlineResource> OFFLINE_RESOURCES = EnumSet.noneOf(OnlineResource.class); 225 220 226 221 /** 227 222 * Logging level (5 = trace, 4 = debug, 3 = info, 2 = warn, 1 = error, 0 = none). 228 223 * @since 6248 224 * @deprecated Use {@link Logging} class. 229 225 */ 226 @Deprecated 230 227 public static int logLevel = 3; 231 228 232 229 /** … … public abstract class Main { 235 232 */ 236 233 protected static final MainPanel mainPanel = new MainPanel(getLayerManager()); 237 234 238 private static void rememberWarnErrorMsg(String msg) {239 // Only remember first line of message240 int idx = msg.indexOf('\n');241 if (idx > 0) {242 ERRORS_AND_WARNINGS.add(msg.substring(0, idx));243 } else {244 ERRORS_AND_WARNINGS.add(msg);245 }246 // Only keep 10 lines to avoid memory leak247 while (ERRORS_AND_WARNINGS.size() > 10) {248 ERRORS_AND_WARNINGS.remove(0);249 }250 }251 252 235 /** 253 236 * Replies the first lines of last 5 error and warning messages, used for bug reports 254 237 * @return the first lines of last 5 error and warning messages 255 238 * @since 7420 256 239 */ 257 240 public static final Collection<String> getLastErrorAndWarnings() { 258 return Collections.unmodifiableList(ERRORS_AND_WARNINGS);241 return Logging.getLastErrorAndWarnings(); 259 242 } 260 243 261 244 /** … … public abstract class Main { 263 246 * @since 8959 264 247 */ 265 248 public static void clearLastErrorAndWarnings() { 266 ERRORS_AND_WARNINGS.clear();249 Logging.clearLastErrorAndWarnings(); 267 250 } 268 251 269 252 /** … … public abstract class Main { 272 255 * @since 6248 273 256 */ 274 257 public static void error(String msg) { 275 if (logLevel < 1) 276 return; 277 if (msg != null && !msg.isEmpty()) { 278 System.err.println(tr("ERROR: {0}", msg)); 279 rememberWarnErrorMsg("E: "+msg); 280 } 258 Logging.error(msg); 281 259 } 282 260 283 261 /** … … public abstract class Main { 285 263 * @param msg The message to print. 286 264 */ 287 265 public static void warn(String msg) { 288 if (logLevel < 2) 289 return; 290 if (msg != null && !msg.isEmpty()) { 291 System.err.println(tr("WARNING: {0}", msg)); 292 rememberWarnErrorMsg("W: "+msg); 293 } 266 Logging.warn(msg); 294 267 } 295 268 296 269 /** … … public abstract class Main { 298 271 * @param msg The message to print. 299 272 */ 300 273 public static void info(String msg) { 301 if (logLevel < 3) 302 return; 303 if (msg != null && !msg.isEmpty()) { 304 System.out.println(tr("INFO: {0}", msg)); 305 } 274 Logging.info(msg); 306 275 } 307 276 308 277 /** … … public abstract class Main { 310 279 * @param msg The message to print. 311 280 */ 312 281 public static void debug(String msg) { 313 if (logLevel < 4) 314 return; 315 if (msg != null && !msg.isEmpty()) { 316 System.out.println(tr("DEBUG: {0}", msg)); 317 } 282 Logging.debug(msg); 318 283 } 319 284 320 285 /** … … public abstract class Main { 322 287 * @param msg The message to print. 323 288 */ 324 289 public static void trace(String msg) { 325 if (logLevel < 5) 326 return; 327 if (msg != null && !msg.isEmpty()) { 328 System.out.print("TRACE: "); 329 System.out.println(msg); 330 } 290 Logging.trace(msg); 331 291 } 332 292 333 293 /** … … public abstract class Main { 337 297 * @since 6852 338 298 */ 339 299 public static boolean isDebugEnabled() { 340 return logLevel >= 4;300 return Logging.isLoggingEnabled(Logging.LEVEL_DEBUG); 341 301 } 342 302 343 303 /** … … public abstract class Main { 347 307 * @since 6852 348 308 */ 349 309 public static boolean isTraceEnabled() { 350 return logLevel >= 5;310 return Logging.isLoggingEnabled(Logging.LEVEL_TRACE); 351 311 } 352 312 353 313 /** … … public abstract class Main { 358 318 * @since 6248 359 319 */ 360 320 public static void error(String msg, Object... objects) { 361 error(MessageFormat.format(msg, objects));321 Logging.error(msg, objects); 362 322 } 363 323 364 324 /** … … public abstract class Main { 368 328 * @param objects The objects to insert into format string. 369 329 */ 370 330 public static void warn(String msg, Object... objects) { 371 warn(MessageFormat.format(msg, objects));331 Logging.warn(msg, objects); 372 332 } 373 333 374 334 /** … … public abstract class Main { 378 338 * @param objects The objects to insert into format string. 379 339 */ 380 340 public static void info(String msg, Object... objects) { 381 info(MessageFormat.format(msg, objects));341 Logging.info(msg, objects); 382 342 } 383 343 384 344 /** … … public abstract class Main { 388 348 * @param objects The objects to insert into format string. 389 349 */ 390 350 public static void debug(String msg, Object... objects) { 391 debug(MessageFormat.format(msg, objects));351 Logging.debug(msg, objects); 392 352 } 393 353 394 354 /** … … public abstract class Main { 398 358 * @param objects The objects to insert into format string. 399 359 */ 400 360 public static void trace(String msg, Object... objects) { 401 trace(MessageFormat.format(msg, objects));361 Logging.trace(msg, objects); 402 362 } 403 363 404 364 /** … … public abstract class Main { 407 367 * @since 6248 408 368 */ 409 369 public static void error(Throwable t) { 410 error(t, true);370 Logging.logWithStackTrace(Logging.LEVEL_ERROR, t); 411 371 } 412 372 413 373 /** … … public abstract class Main { 416 376 * @since 6248 417 377 */ 418 378 public static void warn(Throwable t) { 419 warn(t, true);379 Logging.logWithStackTrace(Logging.LEVEL_WARN, t); 420 380 } 421 381 422 382 /** … … public abstract class Main { 425 385 * @since 10420 426 386 */ 427 387 public static void debug(Throwable t) { 428 debug(getErrorMessage(t));388 Logging.log(Logging.LEVEL_DEBUG, t); 429 389 } 430 390 431 391 /** … … public abstract class Main { 434 394 * @since 10420 435 395 */ 436 396 public static void trace(Throwable t) { 437 trace(getErrorMessage(t));397 Logging.log(Logging.LEVEL_TRACE, t); 438 398 } 439 399 440 400 /** … … public abstract class Main { 444 404 * @since 6642 445 405 */ 446 406 public static void error(Throwable t, boolean stackTrace) { 447 error(getErrorMessage(t));448 407 if (stackTrace) { 449 t.printStackTrace(); 408 Logging.log(Logging.LEVEL_ERROR, t); 409 } else { 410 Logging.logWithStackTrace(Logging.LEVEL_ERROR, t); 450 411 } 451 412 } 452 413 … … public abstract class Main { 457 418 * @since 10420 458 419 */ 459 420 public static void error(Throwable t, String message) { 460 warn(message + ' ' + getErrorMessage(t));421 Logging.log(Logging.LEVEL_ERROR, message, t); 461 422 } 462 423 463 424 /** … … public abstract class Main { 467 428 * @since 6642 468 429 */ 469 430 public static void warn(Throwable t, boolean stackTrace) { 470 warn(getErrorMessage(t));471 431 if (stackTrace) { 472 t.printStackTrace(); 432 Logging.log(Logging.LEVEL_WARN, t); 433 } else { 434 Logging.logWithStackTrace(Logging.LEVEL_WARN, t); 473 435 } 474 436 } 475 437 … … public abstract class Main { 480 442 * @since 10420 481 443 */ 482 444 public static void warn(Throwable t, String message) { 483 warn(message + ' ' + getErrorMessage(t));445 Logging.log(Logging.LEVEL_WARN, message, t); 484 446 } 485 447 486 448 /** … … public abstract class Main { 492 454 public static String getErrorMessage(Throwable t) { 493 455 if (t == null) { 494 456 return null; 457 } else { 458 return Logging.getErrorMessage(t); 495 459 } 496 StringBuilder sb = new StringBuilder(t.getClass().getName());497 String msg = t.getMessage();498 if (msg != null) {499 sb.append(": ").append(msg.trim());500 }501 Throwable cause = t.getCause();502 if (cause != null && !cause.equals(t)) {503 sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause));504 }505 return sb.toString();506 460 } 507 461 508 462 /** … … public abstract class Main { 570 524 public void initialize() { 571 525 fileWatcher.start(); 572 526 573 new InitializationTask(tr("Executing platform startup hook")) { 574 @Override 575 public void initialize() { 576 platform.startupHook(); 577 } 578 }.call(); 579 580 new InitializationTask(tr("Building main menu")) { 527 new InitializationTask(tr("Executing platform startup hook"), platform::startupHook).call(); 581 528 582 @Override 583 public void initialize() { 584 initializeMainWindow(); 585 } 586 }.call(); 529 new InitializationTask(tr("Building main menu"), this::initializeMainWindow).call(); 587 530 588 531 undoRedo.addCommandQueueListener(redoUndoListener); 589 532 … … public abstract class Main { 596 539 // contains several initialization tasks to be executed (in parallel) by a ExecutorService 597 540 List<Callable<Void>> tasks = new ArrayList<>(); 598 541 599 tasks.add(new InitializationTask(tr("Initializing OSM API")) { 600 601 @Override 602 public void initialize() { 542 tasks.add(new InitializationTask(tr("Initializing OSM API"), () -> { 603 543 // We try to establish an API connection early, so that any API 604 544 // capabilities are already known to the editor instance. However 605 545 // if it goes wrong that's not critical at this stage. … … public abstract class Main { 608 548 } catch (OsmTransferCanceledException | OsmApiInitializationException e) { 609 549 Main.warn(getErrorMessage(Utils.getRootCause(e))); 610 550 } 611 } 612 }); 551 })); 613 552 614 tasks.add(new InitializationTask(tr("Initializing validator") ) {553 tasks.add(new InitializationTask(tr("Initializing validator"), OsmValidator::initialize)); 615 554 616 @Override 617 public void initialize() { 618 OsmValidator.initialize(); 619 } 620 }); 621 622 tasks.add(new InitializationTask(tr("Initializing presets")) { 623 624 @Override 625 public void initialize() { 626 TaggingPresets.initialize(); 627 } 628 }); 555 tasks.add(new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize)); 629 556 630 tasks.add(new InitializationTask(tr("Initializing map styles") ) {557 tasks.add(new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize)); 631 558 632 @Override 633 public void initialize() { 634 MapPaintPreference.initialize(); 635 } 636 }); 637 638 tasks.add(new InitializationTask(tr("Loading imagery preferences")) { 639 640 @Override 641 public void initialize() { 642 ImageryPreference.initialize(); 643 } 644 }); 559 tasks.add(new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize)); 645 560 646 561 try { 647 finalExecutorService service = Executors.newFixedThreadPool(562 ExecutorService service = Executors.newFixedThreadPool( 648 563 Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY)); 649 564 for (Future<Void> i : service.invokeAll(tasks)) { 650 565 i.get(); … … public abstract class Main { 657 572 // hooks for the jmapviewer component 658 573 FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl); 659 574 FeatureAdapter.registerTranslationAdapter(I18n.getTranslationAdapter()); 660 FeatureAdapter.registerLoggingAdapter(name -> { 661 Logger logger = Logger.getAnonymousLogger(); 662 logger.setUseParentHandlers(false); 663 logger.setLevel(Level.ALL); 664 if (logger.getHandlers().length == 0) { 665 logger.addHandler(new Handler() { 666 @Override 667 public void publish(LogRecord record) { 668 String msg = MessageFormat.format(record.getMessage(), record.getParameters()); 669 if (record.getLevel().intValue() >= Level.SEVERE.intValue()) { 670 Main.error(msg); 671 } else if (record.getLevel().intValue() >= Level.WARNING.intValue()) { 672 Main.warn(msg); 673 } else if (record.getLevel().intValue() >= Level.INFO.intValue()) { 674 Main.info(msg); 675 } else if (record.getLevel().intValue() >= Level.FINE.intValue()) { 676 Main.debug(msg); 677 } else { 678 Main.trace(msg); 679 } 680 } 681 682 @Override 683 public void flush() { 684 // Do nothing 685 } 575 FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger()); 686 576 687 @Override 688 public void close() { 689 // Do nothing 690 } 691 }); 692 } 693 return logger; 694 }); 695 696 new InitializationTask(tr("Updating user interface")) { 697 698 @Override 699 public void initialize() { 700 toolbar.refreshToolbarControl(); 701 toolbar.control.updateUI(); 702 contentPanePrivate.updateUI(); 703 } 704 }.call(); 577 new InitializationTask(tr("Updating user interface"), () -> { 578 toolbar.refreshToolbarControl(); 579 toolbar.control.updateUI(); 580 contentPanePrivate.updateUI(); 581 }).call(); 705 582 } 706 583 707 584 /** … … public abstract class Main { 712 589 // can be implementd by subclasses 713 590 } 714 591 715 private abstractstatic class InitializationTask implements Callable<Void> {592 private static class InitializationTask implements Callable<Void> { 716 593 717 594 private final String name; 595 private Runnable task; 718 596 719 protected InitializationTask(String name ) {597 protected InitializationTask(String name, Runnable task) { 720 598 this.name = name; 599 this.task = task; 721 600 } 722 601 723 public abstract void initialize();724 725 602 @Override 726 603 public Void call() { 727 604 Object status = null; 728 605 if (initListener != null) { 729 606 status = initListener.updateStatus(name); 730 607 } 731 initialize();608 task.run(); 732 609 if (initListener != null) { 733 610 initListener.finish(status); 734 611 } … … public abstract class Main { 950 827 * Should be called before the main constructor to setup some parameter stuff 951 828 * @param args The parsed argument list. 952 829 */ 953 public static void preConstructorInit( Map<Option, Collection<String>>args) {830 public static void preConstructorInit(ProgramArguments args) { 954 831 ProjectionPreference.setProjection(); 955 832 956 833 String defaultlaf = platform.getDefaultStyle(); … … public abstract class Main { 1017 894 } 1018 895 } 1019 896 1020 protected static void postConstructorProcessCmdLine(Map<Option, Collection<String>> args) { 1021 if (args.containsKey(Option.DOWNLOAD)) { 1022 List<File> fileList = new ArrayList<>(); 1023 for (String s : args.get(Option.DOWNLOAD)) { 1024 DownloadParamType.paramType(s).download(s, fileList); 1025 } 1026 if (!fileList.isEmpty()) { 1027 OpenFileAction.openFiles(fileList, true); 1028 } 897 protected static void postConstructorProcessCmdLine(ProgramArguments args) { 898 List<File> fileList = new ArrayList<>(); 899 for (String s : args.get(Option.DOWNLOAD)) { 900 DownloadParamType.paramType(s).download(s, fileList); 1029 901 } 1030 if (args.containsKey(Option.DOWNLOADGPS)) { 1031 for (String s : args.get(Option.DOWNLOADGPS)) { 1032 DownloadParamType.paramType(s).downloadGps(s); 1033 } 902 if (!fileList.isEmpty()) { 903 OpenFileAction.openFiles(fileList, true); 1034 904 } 1035 if (args.containsKey(Option.SELECTION)) { 1036 for (String s : args.get(Option.SELECTION)) { 1037 SearchAction.search(s, SearchAction.SearchMode.add); 1038 } 905 for (String s : args.get(Option.DOWNLOADGPS)) { 906 DownloadParamType.paramType(s).downloadGps(s); 907 } 908 for (String s : args.get(Option.SELECTION)) { 909 SearchAction.search(s, SearchAction.SearchMode.add); 1039 910 } 1040 911 } 1041 912 … … public abstract class Main { 1488 1359 public static void setup() { 1489 1360 if (!windowSwitchListeners.isEmpty()) { 1490 1361 for (Window w : Window.getWindows()) { 1491 if (w.isShowing()) { 1492 if (!Arrays.asList(w.getWindowListeners()).contains(getInstance())) { 1493 w.addWindowListener(getInstance()); 1494 } 1362 if (w.isShowing() && !Arrays.asList(w.getWindowListeners()).contains(getInstance())) { 1363 w.addWindowListener(getInstance()); 1495 1364 } 1496 1365 } 1497 1366 } -
src/org/openstreetmap/josm/gui/MainApplication.java
diff --git a/src/org/openstreetmap/josm/gui/MainApplication.java b/src/org/openstreetmap/josm/gui/MainApplication.java index e42444a..4395c0a 100644
a b import java.security.PermissionCollection; 24 24 import java.security.Permissions; 25 25 import java.security.Policy; 26 26 import java.security.cert.CertificateException; 27 import java.util.ArrayList;28 27 import java.util.Arrays; 29 28 import java.util.Collection; 30 import java.util.EnumMap;31 29 import java.util.List; 32 30 import java.util.Locale; 33 import java.util. Map;31 import java.util.Optional; 34 32 import java.util.Set; 35 33 import java.util.TreeSet; 34 import java.util.logging.Level; 36 35 37 36 import javax.swing.JOptionPane; 38 37 import javax.swing.RepaintManager; … … import org.openstreetmap.josm.actions.RestartAction; 45 44 import org.openstreetmap.josm.data.AutosaveTask; 46 45 import org.openstreetmap.josm.data.CustomConfigurator; 47 46 import org.openstreetmap.josm.data.Version; 47 import org.openstreetmap.josm.gui.ProgramArguments.Option; 48 48 import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor; 49 49 import org.openstreetmap.josm.gui.download.DownloadDialog; 50 50 import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; … … import org.openstreetmap.josm.plugins.PluginInformation; 62 62 import org.openstreetmap.josm.tools.FontsManager; 63 63 import org.openstreetmap.josm.tools.HttpClient; 64 64 import org.openstreetmap.josm.tools.I18n; 65 import org.openstreetmap.josm.tools.Logging; 65 66 import org.openstreetmap.josm.tools.OsmUrlToBounds; 66 67 import org.openstreetmap.josm.tools.PlatformHookWindows; 67 68 import org.openstreetmap.josm.tools.Utils; 68 69 import org.openstreetmap.josm.tools.WindowGeometry; 70 import org.openstreetmap.josm.tools.bugreport.BugReport; 69 71 import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; 70 72 71 import gnu.getopt.Getopt;72 import gnu.getopt.LongOpt;73 74 73 /** 75 74 * Main window class application. 76 75 * … … public class MainApplication extends Main { 170 169 } 171 170 172 171 /** 173 * JOSM command line options.174 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a>175 * @since 5279176 */177 public enum Option {178 /** --help|-h Show this help */179 HELP(false),180 /** --version Displays the JOSM version and exits */181 VERSION(false),182 /** --debug Print debugging messages to console */183 DEBUG(false),184 /** --trace Print detailed debugging messages to console */185 TRACE(false),186 /** --language=<language> Set the language */187 LANGUAGE(true),188 /** --reset-preferences Reset the preferences to default */189 RESET_PREFERENCES(false),190 /** --load-preferences=<url-to-xml> Changes preferences according to the XML file */191 LOAD_PREFERENCES(true),192 /** --set=<key>=<value> Set preference key to value */193 SET(true),194 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */195 GEOMETRY(true),196 /** --no-maximize Do not launch in maximized mode */197 NO_MAXIMIZE(false),198 /** --maximize Launch in maximized mode */199 MAXIMIZE(false),200 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br>201 * --download=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) <br>202 * --download=<filename> Open a file (any file type that can be opened with File/Open) */203 DOWNLOAD(true),204 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br>205 * --downloadgps=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS */206 DOWNLOADGPS(true),207 /** --selection=<searchstring> Select with the given search */208 SELECTION(true),209 /** --offline=<osm_api|josm_website|all> Disable access to the given resource(s), delimited by comma */210 OFFLINE(true),211 /** --skip-plugins */212 SKIP_PLUGINS(false);213 214 private final String name;215 private final boolean requiresArg;216 217 Option(boolean requiresArgument) {218 this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-');219 this.requiresArg = requiresArgument;220 }221 222 /**223 * Replies the option name224 * @return The option name, in lowercase225 */226 public String getName() {227 return name;228 }229 230 /**231 * Determines if this option requires an argument.232 * @return {@code true} if this option requires an argument, {@code false} otherwise233 */234 public boolean requiresArgument() {235 return requiresArg;236 }237 }238 239 /**240 * Builds the command-line argument map.241 * @param args command-line arguments array242 * @return command-line argument map243 */244 public static Map<Option, Collection<String>> buildCommandLineArgumentMap(String ... args) {245 246 List<LongOpt> los = new ArrayList<>();247 for (Option o : Option.values()) {248 los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0));249 }250 251 Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()]));252 253 Map<Option, Collection<String>> argMap = new EnumMap<>(Option.class);254 255 int c;256 while ((c = g.getopt()) != -1) {257 Option opt;258 switch (c) {259 case 'h':260 opt = Option.HELP;261 break;262 case 'v':263 opt = Option.VERSION;264 break;265 case 0:266 opt = Option.values()[g.getLongind()];267 break;268 default:269 opt = null;270 }271 if (opt != null) {272 Collection<String> values = argMap.get(opt);273 if (values == null) {274 values = new ArrayList<>();275 argMap.put(opt, values);276 }277 values.add(g.getOptarg());278 } else279 throw new IllegalArgumentException("Invalid option: "+c);280 }281 // positional arguments are a shortcut for the --download ... option282 for (int i = g.getOptind(); i < args.length; ++i) {283 Collection<String> values = argMap.get(Option.DOWNLOAD);284 if (values == null) {285 values = new ArrayList<>();286 argMap.put(Option.DOWNLOAD, values);287 }288 values.add(args[i]);289 }290 291 return argMap;292 }293 294 /**295 172 * Main application Startup 296 173 * @param argArray Command-line arguments 297 174 */ … … public class MainApplication extends Main { 299 176 I18n.init(); 300 177 301 178 // construct argument table 302 Map<Option, Collection<String>>args = null;179 ProgramArguments args = null; 303 180 try { 304 args = buildCommandLineArgumentMap(argArray);181 args = new ProgramArguments(argArray); 305 182 } catch (IllegalArgumentException e) { 306 183 System.exit(1); 307 184 return; 308 185 } 309 186 310 final boolean languageGiven = args.containsKey(Option.LANGUAGE); 311 312 if (languageGiven) { 313 I18n.set(args.get(Option.LANGUAGE).iterator().next()); 314 } 187 Level logLevel = args.getLogLevel(); 188 Logging.setLogLevel(logLevel); 189 Main.info(tr("Log level is at ", logLevel)); 315 190 316 if (args.containsKey(Option.TRACE)) { 317 // Enable JOSM debug level 318 logLevel = 5; 319 // Enable debug in OAuth signpost via system preference, but only at trace level 320 Utils.updateSystemProperty("debug", "true"); 321 Main.info(tr("Enabled detailed debug level (trace)")); 322 } else if (args.containsKey(Option.DEBUG)) { 323 // Enable JOSM debug level 324 logLevel = 4; 325 Main.info(tr("Printing debugging messages to console")); 326 } 191 Optional<String> language = args.getSingle(Option.LANGUAGE); 192 I18n.set(language.orElse(null)); 327 193 328 194 Policy.setPolicy(new Policy() { 329 195 // Permissions for plug-ins loaded when josm is started via webstart … … public class MainApplication extends Main { 349 215 350 216 Main.COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray)); 351 217 352 if (args. containsKey(Option.VERSION)) {218 if (args.showVersion()) { 353 219 System.out.println(Version.getInstance().getAgentString()); 354 220 System.exit(0); 221 } else if (args.showHelp()) { 222 showHelp(); 223 System.exit(0); 355 224 } 356 225 357 boolean skipLoadingPlugins = false; 358 if (args.containsKey(Option.SKIP_PLUGINS)) { 359 skipLoadingPlugins = true; 226 boolean skipLoadingPlugins = args.hasOption(Option.SKIP_PLUGINS); 227 if (skipLoadingPlugins) { 360 228 Main.info(tr("Plugin loading skipped")); 361 229 } 362 230 363 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES)); 364 365 if (args.containsKey(Option.SET)) { 366 for (String i : args.get(Option.SET)) { 367 String[] kv = i.split("=", 2); 368 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]); 369 } 231 if (Logging.isLoggingEnabled(Logging.LEVEL_TRACE)) { 232 // Enable debug in OAuth signpost via system preference, but only at trace level 233 Utils.updateSystemProperty("debug", "true"); 234 Main.info(tr("Enabled detailed debug level (trace)")); 370 235 } 371 236 372 if (!languageGiven) { 237 Main.pref.init(args.hasOption(Option.RESET_PREFERENCES)); 238 239 args.getPreferencesToSet().forEach(Main.pref::put); 240 241 if (!language.isPresent()) { 373 242 I18n.set(Main.pref.get("language", null)); 374 243 } 375 244 Main.pref.updateSystemProperties(); … … public class MainApplication extends Main { 377 246 checkIPv6(); 378 247 379 248 // asking for help? show help and exit 380 if (args. containsKey(Option.HELP)) {249 if (args.hasOption(Option.HELP)) { 381 250 showHelp(); 382 251 System.exit(0); 383 252 } … … public class MainApplication extends Main { 391 260 I18n.setupLanguageFonts(); 392 261 393 262 WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry", 394 args. containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null,395 !args. containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));263 args.getSingle(Option.GEOMETRY).orElse(null), 264 !args.hasOption(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false)); 396 265 final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry); 397 266 Main.parent = mainFrame; 398 267 399 if (args. containsKey(Option.LOAD_PREFERENCES)) {268 if (args.hasOption(Option.LOAD_PREFERENCES)) { 400 269 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 401 270 for (String i : args.get(Option.LOAD_PREFERENCES)) { 402 271 info("Reading preferences from " + i); 403 272 try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) { 404 273 config.openAndReadXML(is); 405 274 } catch (IOException ex) { 406 throw new RuntimeException(ex);275 throw BugReport.intercept(ex).put("file", i); 407 276 } 408 277 } 409 278 } … … public class MainApplication extends Main { 466 335 Main.MasterWindowListener.setup(); 467 336 468 337 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 469 if ((!args. containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {338 if ((!args.hasOption(Option.NO_MAXIMIZE) && maximized) || args.hasOption(Option.MAXIMIZE)) { 470 339 mainFrame.setMaximized(true); 471 340 } 472 341 if (main.menu.fullscreenToggleAction != null) { … … public class MainApplication extends Main { 524 393 toolbar.refreshToolbarControl(); 525 394 } 526 395 527 private static void processOffline( Map<Option, Collection<String>>args) {528 if (args.containsKey(Option.OFFLINE)) {529 for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) {396 private static void processOffline(ProgramArguments args) { 397 for (String offlineNames : args.get(Option.OFFLINE)) { 398 for (String s : offlineNames.split(",")) { 530 399 try { 531 400 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 532 401 } catch (IllegalArgumentException e) { … … public class MainApplication extends Main { 536 405 return; 537 406 } 538 407 } 539 Set<OnlineResource> offline = Main.getOfflineResources();540 if (!offline.isEmpty()) {541 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",542 "JOSM is running in offline mode. These resourceswill not be available: {0}",543 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));544 }408 } 409 Set<OnlineResource> offline = Main.getOfflineResources(); 410 if (!offline.isEmpty()) { 411 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 412 "JOSM is running in offline mode. These resources will not be available: {0}", 413 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 545 414 } 546 415 } 547 416 … … public class MainApplication extends Main { 596 465 597 466 private static class GuiFinalizationWorker implements Runnable { 598 467 599 private final Map<Option, Collection<String>>args;468 private final ProgramArguments args; 600 469 private final DefaultProxySelector proxySelector; 601 470 602 GuiFinalizationWorker( Map<Option, Collection<String>>args, DefaultProxySelector proxySelector) {471 GuiFinalizationWorker(ProgramArguments args, DefaultProxySelector proxySelector) { 603 472 this.args = args; 604 473 this.proxySelector = proxySelector; 605 474 } -
new file src/org/openstreetmap/josm/gui/ProgramArguments.java
diff --git a/src/org/openstreetmap/josm/gui/ProgramArguments.java b/src/org/openstreetmap/josm/gui/ProgramArguments.java new file mode 100644 index 0000000..cf446f2
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui; 3 4 import java.util.ArrayList; 5 import java.util.Collection; 6 import java.util.Collections; 7 import java.util.EnumMap; 8 import java.util.HashMap; 9 import java.util.List; 10 import java.util.Locale; 11 import java.util.Map; 12 import java.util.Optional; 13 import java.util.logging.Level; 14 import java.util.stream.Stream; 15 16 import org.openstreetmap.josm.tools.Logging; 17 18 import gnu.getopt.Getopt; 19 import gnu.getopt.LongOpt; 20 21 /** 22 * This class holds the arguments passed on to Main. 23 * @author Michael Zangl 24 * @since xxx 25 */ 26 public class ProgramArguments { 27 28 /** 29 * JOSM command line options. 30 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a> 31 * @since xxx 32 */ 33 public enum Option { 34 /** --help|-h Show this help */ 35 HELP(false), 36 /** --version Displays the JOSM version and exits */ 37 VERSION(false), 38 /** --debug Print debugging messages to console */ 39 DEBUG(false), 40 /** --trace Print detailed debugging messages to console */ 41 TRACE(false), 42 /** --language=<language> Set the language */ 43 LANGUAGE(true), 44 /** --reset-preferences Reset the preferences to default */ 45 RESET_PREFERENCES(false), 46 /** --load-preferences=<url-to-xml> Changes preferences according to the XML file */ 47 LOAD_PREFERENCES(true), 48 /** --set=<key>=<value> Set preference key to value */ 49 SET(true), 50 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */ 51 GEOMETRY(true), 52 /** --no-maximize Do not launch in maximized mode */ 53 NO_MAXIMIZE(false), 54 /** --maximize Launch in maximized mode */ 55 MAXIMIZE(false), 56 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br> 57 * --download=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) <br> 58 * --download=<filename> Open a file (any file type that can be opened with File/Open) */ 59 DOWNLOAD(true), 60 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br> 61 * --downloadgps=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS */ 62 DOWNLOADGPS(true), 63 /** --selection=<searchstring> Select with the given search */ 64 SELECTION(true), 65 /** --offline=<osm_api|josm_website|all> Disable access to the given resource(s), delimited by comma */ 66 OFFLINE(true), 67 /** --skip-plugins */ 68 SKIP_PLUGINS(false); 69 70 private final String name; 71 private final boolean requiresArg; 72 73 Option(boolean requiresArgument) { 74 this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-'); 75 this.requiresArg = requiresArgument; 76 } 77 78 /** 79 * Replies the option name 80 * @return The option name, in lowercase 81 */ 82 public String getName() { 83 return name; 84 } 85 86 /** 87 * Determines if this option requires an argument. 88 * @return {@code true} if this option requires an argument, {@code false} otherwise 89 */ 90 public boolean requiresArgument() { 91 return requiresArg; 92 } 93 94 LongOpt toLongOpt() { 95 return new LongOpt(getName(), requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0); 96 } 97 } 98 99 private final Map<Option, List<String>> argMap = new EnumMap<>(Option.class); 100 101 /** 102 * Construct the program arguments object 103 * @param args The args passed to main. 104 */ 105 public ProgramArguments(String[] args) { 106 Stream.of(Option.values()).forEach(o -> argMap.put(o, new ArrayList<>())); 107 108 buildCommandLineArgumentMap(args); 109 } 110 111 /** 112 * Builds the command-line argument map. 113 * @param args command-line arguments array 114 */ 115 private void buildCommandLineArgumentMap(String ... args) { 116 LongOpt[] los = Stream.of(Option.values()).map(Option::toLongOpt).toArray(i -> new LongOpt[i]); 117 118 Getopt g = new Getopt("JOSM", args, "hv", los); 119 120 int c; 121 while ((c = g.getopt()) != -1) { 122 Option opt; 123 switch (c) { 124 case 'h': 125 opt = Option.HELP; 126 break; 127 case 'v': 128 opt = Option.VERSION; 129 break; 130 case 0: 131 opt = Option.values()[g.getLongind()]; 132 break; 133 default: 134 opt = null; 135 } 136 if (opt != null) { 137 addOption(opt, g.getOptarg()); 138 } else 139 throw new IllegalArgumentException("Invalid option: "+c); 140 } 141 // positional arguments are a shortcut for the --download ... option 142 for (int i = g.getOptind(); i < args.length; ++i) { 143 addOption(Option.DOWNLOAD, args[i]); 144 } 145 } 146 147 private void addOption(Option opt, String optarg) { 148 argMap.get(opt).add(optarg); 149 } 150 151 /** 152 * Gets a single argument (the first) that was given for the given option. 153 * @param option The option to search 154 * @return The argument as optional value. 155 */ 156 public Optional<String> getSingle(Option option) { 157 return get(option).stream().findFirst(); 158 } 159 160 /** 161 * Gets all values that are given for a given option 162 * @param option The option 163 * @return The values that were given. May be empty. 164 */ 165 public Collection<String> get(Option option) { 166 return Collections.unmodifiableList(argMap.get(option)); 167 } 168 169 /** 170 * Test if a given option was used by the user. 171 * @param option The option to test for 172 * @return <code>true</code> if the user used it. 173 */ 174 public boolean hasOption(Option option) { 175 return !get(option).isEmpty(); 176 } 177 178 /** 179 * Helper method to indicate if version should be displayed. 180 * @return <code>true</code> to display version 181 */ 182 public boolean showVersion() { 183 return hasOption(Option.VERSION); 184 } 185 186 /** 187 * Helper method to indicate if help should be displayed. 188 * @return <code>true</code> to display version 189 */ 190 public boolean showHelp() { 191 return !get(Option.HELP).isEmpty(); 192 } 193 194 /** 195 * Get the log level the user wants us to use. 196 * @return The log level. 197 */ 198 public Level getLogLevel() { 199 if (hasOption(Option.TRACE)) { 200 return Logging.LEVEL_TRACE; 201 } else if (hasOption(Option.DEBUG)) { 202 return Logging.LEVEL_DEBUG; 203 } else { 204 return Logging.LEVEL_INFO; 205 } 206 } 207 208 /** 209 * Gets a map of all preferences the user wants to set. 210 * @return The preferences to set. It contains null values for preferences to unset 211 */ 212 public Map<String, String> getPreferencesToSet() { 213 HashMap<String, String> map = new HashMap<>(); 214 get(Option.SET).stream().map(i -> i.split("=", 2)).forEach(kv -> map.put(kv[0], getValue(kv))); 215 return map; 216 } 217 218 private static String getValue(String[] kv) { 219 if (kv.length < 2) { 220 return ""; 221 } else if ("null".equals(kv[1])) { 222 return null; 223 } else { 224 return kv[1]; 225 } 226 } 227 } -
new file src/org/openstreetmap/josm/tools/Logging.java
diff --git a/src/org/openstreetmap/josm/tools/Logging.java b/src/org/openstreetmap/josm/tools/Logging.java new file mode 100644 index 0000000..7c7728e
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.io.OutputStream; 7 import java.io.PrintWriter; 8 import java.io.StringWriter; 9 import java.text.MessageFormat; 10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.List; 13 import java.util.function.Supplier; 14 import java.util.logging.ConsoleHandler; 15 import java.util.logging.Handler; 16 import java.util.logging.Level; 17 import java.util.logging.LogRecord; 18 import java.util.logging.Logger; 19 20 import org.openstreetmap.josm.tools.bugreport.BugReport; 21 22 /** 23 * This class contains utility methods to log errors and warnings. 24 * <p> 25 * There are multiple log levels supported. 26 * @author Michael Zangl 27 * @since xxx 28 */ 29 public final class Logging { 30 /** 31 * The josm internal log level indicating a severe error in the application that usually leads to a crash. 32 */ 33 public static final Level LEVEL_ERROR = Level.SEVERE; 34 /** 35 * The josm internal log level to use when something that may lead to a crash or wrong behaviour has happened. 36 */ 37 public static final Level LEVEL_WARN = Level.WARNING; 38 /** 39 * The josm internal log level to use for important events that will be useful when debugging problems 40 */ 41 public static final Level LEVEL_INFO = Level.INFO; 42 /** 43 * The josm internal log level to print debug output 44 */ 45 public static final Level LEVEL_DEBUG = Level.FINE; 46 /** 47 * The finest log level josm supports. This lets josm print a lot of debug output. 48 */ 49 public static final Level LEVEL_TRACE = Level.FINEST; 50 private static final Logger LOGGER = Logger.getAnonymousLogger(); 51 private static final RememberWarningHandler WARNINGS = new RememberWarningHandler(); 52 53 static { 54 LOGGER.setLevel(Level.ALL); 55 LOGGER.setUseParentHandlers(false); 56 57 ConsoleHandler stderr = new ConsoleHandler(); 58 LOGGER.addHandler(stderr); 59 stderr.setLevel(LEVEL_WARN); 60 61 ConsoleHandler stdout = new ConsoleHandler() { 62 @Override 63 protected synchronized void setOutputStream(OutputStream out) { 64 // overwrite output stream. 65 super.setOutputStream(System.out); 66 } 67 68 @Override 69 public void publish(LogRecord record) { 70 if (!stderr.isLoggable(record)) { 71 super.publish(record); 72 } 73 } 74 }; 75 LOGGER.addHandler(stdout); 76 stdout.setLevel(Level.ALL); 77 78 LOGGER.addHandler(WARNINGS); 79 } 80 81 private Logging() { 82 // hide 83 } 84 85 /** 86 * Set the global log level. 87 * @param level The log level to use 88 */ 89 public static void setLogLevel(Level level) { 90 LOGGER.setLevel(level); 91 } 92 93 /** 94 * Prints an error message if logging is on. 95 * @param message The message to print. 96 */ 97 public static void error(String message) { 98 logPrivate(LEVEL_ERROR, message); 99 } 100 101 /** 102 * Prints a formatted error message if logging is on. Calls {@link MessageFormat#format} 103 * function to format text. 104 * @param pattern The formatted message to print. 105 * @param args The objects to insert into format string. 106 */ 107 public static void error(String pattern, Object... args) { 108 logPrivate(LEVEL_ERROR, pattern, args); 109 } 110 111 /** 112 * Prints a warning message if logging is on. 113 * @param message The message to print. 114 */ 115 public static void warn(String message) { 116 logPrivate(LEVEL_WARN, message); 117 } 118 119 /** 120 * Prints a formatted warning message if logging is on. Calls {@link MessageFormat#format} 121 * function to format text. 122 * @param pattern The formatted message to print. 123 * @param args The objects to insert into format string. 124 */ 125 public static void warn(String pattern, Object... args) { 126 logPrivate(LEVEL_WARN, pattern, args); 127 } 128 129 /** 130 * Prints a info message if logging is on. 131 * @param message The message to print. 132 */ 133 public static void info(String message) { 134 logPrivate(LEVEL_INFO, message); 135 } 136 137 /** 138 * Prints a formatted info message if logging is on. Calls {@link MessageFormat#format} 139 * function to format text. 140 * @param pattern The formatted message to print. 141 * @param args The objects to insert into format string. 142 */ 143 public static void info(String pattern, Object... args) { 144 logPrivate(LEVEL_INFO, pattern, args); 145 } 146 147 /** 148 * Prints a debug message if logging is on. 149 * @param message The message to print. 150 */ 151 public static void debug(String message) { 152 logPrivate(LEVEL_DEBUG, message); 153 } 154 155 /** 156 * Prints a formatted debug message if logging is on. Calls {@link MessageFormat#format} 157 * function to format text. 158 * @param pattern The formatted message to print. 159 * @param args The objects to insert into format string. 160 */ 161 public static void debug(String pattern, Object... args) { 162 logPrivate(LEVEL_DEBUG, pattern, args); 163 } 164 165 /** 166 * Prints a trace message if logging is on. 167 * @param message The message to print. 168 */ 169 public static void trace(String message) { 170 logPrivate(LEVEL_TRACE, message); 171 } 172 173 /** 174 * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format} 175 * function to format text. 176 * @param pattern The formatted message to print. 177 * @param args The objects to insert into format string. 178 */ 179 public static void trace(String pattern, Object... args) { 180 logPrivate(LEVEL_TRACE, pattern, args); 181 } 182 183 /** 184 * Logs a throwable that happened. 185 * @param level The level. 186 * @param t The throwable that should be logged. 187 */ 188 public static void log(Level level, Throwable t) { 189 logPrivate(level, () -> getErrorLog(null, t)); 190 } 191 192 /** 193 * Logs a throwable that happened. 194 * @param level The level. 195 * @param message An additional error message 196 * @param t The throwable that caused the message 197 */ 198 public static void log(Level level, String message, Throwable t) { 199 logPrivate(level, () -> getErrorLog(message, t)); 200 } 201 202 /** 203 * Logs a throwable that happened. Adds the stack trace to the log. 204 * @param level The level. 205 * @param t The throwable that should be logged. 206 */ 207 public static void logWithStackTrace(Level level, Throwable t) { 208 logPrivate(level, () -> getErrorLogWithStack(null, t)); 209 } 210 211 /** 212 * Logs a throwable that happened. Adds the stack trace to the log. 213 * @param level The level. 214 * @param message An additional error message 215 * @param t The throwable that should be logged. 216 */ 217 public static void logWithStackTrace(Level level, String message, Throwable t) { 218 logPrivate(level, () -> getErrorLogWithStack(message, t)); 219 } 220 221 private static void logPrivate(Level level, String pattern, Object... args) { 222 logPrivate(level, () -> MessageFormat.format(pattern, args)); 223 } 224 225 private static void logPrivate(Level level, String message) { 226 logPrivate(level, () -> message); 227 } 228 229 private static void logPrivate(Level level, Supplier<String> supplier) { 230 // all log methods immeadiately call one of the logPrivate methods. 231 if (LOGGER.isLoggable(level)) { 232 StackTraceElement callingMethod = BugReport.getCallingMethod(1, Logging.class.getName(), name -> !"logPrivate".equals(name)); 233 LOGGER.logp(level, callingMethod.getClassName(), callingMethod.getMethodName(), supplier); 234 } 235 } 236 237 /** 238 * Tests if a given log level is enabled. This can be used to avoid constructing debug data if required. 239 * 240 * For formatting text, you should use the {@link #debug(String, Object...)} message 241 * @param level A lvele constant. You can e.g. use {@link Logging#LEVEL_ERROR} 242 * @return <code>true</code> if debug is enabled. 243 */ 244 public static boolean isLoggingEnabled(Level level) { 245 return LOGGER.isLoggable(level); 246 } 247 248 private static String getErrorLog(String message, Throwable t) { 249 StringBuilder sb = new StringBuilder(); 250 if (message != null) { 251 sb.append(message).append(": "); 252 } 253 sb.append(getErrorMessage(t)); 254 return sb.toString(); 255 } 256 257 private static String getErrorLogWithStack(String message, Throwable t) { 258 StringWriter sb = new StringWriter(); 259 sb.append(getErrorLog(message, t)); 260 sb.append('\n'); 261 t.printStackTrace(new PrintWriter(sb)); 262 return sb.toString(); 263 } 264 265 /** 266 * Returns a human-readable message of error, also usable for developers. 267 * @param t The error 268 * @return The human-readable error message 269 */ 270 public static String getErrorMessage(Throwable t) { 271 if (t == null) { 272 return "(no error)"; 273 } 274 StringBuilder sb = new StringBuilder(t.getClass().getName()); 275 String msg = t.getMessage(); 276 if (msg != null) { 277 sb.append(": ").append(msg.trim()); 278 } 279 Throwable cause = t.getCause(); 280 if (cause != null && !cause.equals(t)) { 281 // this may cause infinite loops in the unlikely case that there is a loop in the causes. 282 sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause)); 283 } 284 return sb.toString(); 285 } 286 287 /** 288 * Clear the list of last warnings 289 */ 290 public static void clearLastErrorAndWarnings() { 291 WARNINGS.clear(); 292 } 293 294 /** 295 * Get the last error and warning messages in the order in which they were received. 296 * @return The last errors and warnings. 297 */ 298 public static List<String> getLastErrorAndWarnings() { 299 return WARNINGS.getMessages(); 300 } 301 302 /** 303 * Provides direct access to the logger used. Use of methods like {@link #warn(String)} is prefered. 304 * @return The logger 305 */ 306 public static Logger getLogger() { 307 return LOGGER; 308 } 309 310 private static class RememberWarningHandler extends Handler { 311 private final String[] log = new String[10]; 312 private int messagesLogged; 313 314 RememberWarningHandler() { 315 setLevel(LEVEL_WARN); 316 } 317 318 synchronized void clear() { 319 messagesLogged = 0; 320 Arrays.fill(log, null); 321 } 322 323 @Override 324 public synchronized void publish(LogRecord record) { 325 if (!isLoggable(record)) { 326 return; 327 } 328 329 String msg = getPrefix(record) + record.getMessage(); 330 331 // Only remember first line of message 332 int idx = msg.indexOf('\n'); 333 if (idx > 0) { 334 msg = msg.substring(0, idx); 335 } 336 log[messagesLogged % log.length] = msg; 337 messagesLogged++; 338 } 339 340 private static String getPrefix(LogRecord record) { 341 if (record.getLevel().equals(LEVEL_WARN)) { 342 return "W: "; 343 } else { 344 // worse than warn 345 return "E: "; 346 } 347 } 348 349 synchronized List<String> getMessages() { 350 List<String> logged = Arrays.asList(log); 351 ArrayList<String> res = new ArrayList<>(); 352 int logOffset = messagesLogged % log.length; 353 if (messagesLogged > logOffset) { 354 res.addAll(logged.subList(logOffset, log.length)); 355 } 356 res.addAll(logged.subList(0, logOffset)); 357 return res; 358 } 359 360 @Override 361 public synchronized void flush() { 362 // nothing to do 363 } 364 365 @Override 366 public void close() { 367 // nothing to do 368 } 369 } 370 } -
src/org/openstreetmap/josm/tools/bugreport/BugReport.java
diff --git a/src/org/openstreetmap/josm/tools/bugreport/BugReport.java b/src/org/openstreetmap/josm/tools/bugreport/BugReport.java index 2aa38e1..c2be905 100644
a b import java.io.PrintWriter; 5 5 import java.io.Serializable; 6 6 import java.io.StringWriter; 7 7 import java.util.concurrent.CopyOnWriteArrayList; 8 import java.util.function.Predicate; 8 9 9 10 import org.openstreetmap.josm.actions.ShowStatusReportAction; 10 11 … … public final class BugReport implements Serializable { 184 185 * @return The method name. 185 186 */ 186 187 public static String getCallingMethod(int offset) { 187 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();188 188 String className = BugReport.class.getName(); 189 String methodName = "getCallingMethod"; 190 StackTraceElement found = getCallingMethod(offset, className, methodName::equals); 191 if (found != null) { 192 return found.getClassName().replaceFirst(".*\\.", "") + '#' + found.getMethodName(); 193 } else { 194 return "?"; 195 } 196 } 197 198 /** 199 * Find the method that called the given method on the current stack trace. 200 * @param offset 201 * How many methods to look back in the stack trace. 1 gives the method calling this method, 0 gives you getCallingMethod(). 202 * @param className The name of the class to search for 203 * @param methodName The name of the method to search for 204 * @return The class and method name or null if it is unknown. 205 */ 206 public static StackTraceElement getCallingMethod(int offset, String className, Predicate<String> methodName) { 207 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 189 208 for (int i = 0; i < stackTrace.length - offset; i++) { 190 209 StackTraceElement element = stackTrace[i]; 191 if (className.equals(element.getClassName()) && "getCallingMethod".equals(element.getMethodName())) {210 if (className.equals(element.getClassName()) && methodName.test(element.getMethodName())) { 192 211 StackTraceElement toReturn = stackTrace[i + offset]; 193 return toReturn .getClassName().replaceFirst(".*\\.", "") + '#' + toReturn.getMethodName();212 return toReturn; 194 213 } 195 214 } 196 return "?";215 return null; 197 216 } 198 217 199 218 /** -
test/unit/org/openstreetmap/josm/JOSMFixture.java
diff --git a/test/unit/org/openstreetmap/josm/JOSMFixture.java b/test/unit/org/openstreetmap/josm/JOSMFixture.java index f9bcc23..800af7c 100644
a b import org.openstreetmap.josm.gui.util.GuiHelper; 21 21 import org.openstreetmap.josm.io.CertificateAmendment; 22 22 import org.openstreetmap.josm.io.OsmApi; 23 23 import org.openstreetmap.josm.tools.I18n; 24 import org.openstreetmap.josm.tools.Logging; 24 25 25 26 /** 26 27 * Fixture to define a proper and safe environment before running tests. … … public class JOSMFixture { 98 99 // call the really early hook before we anything else 99 100 Main.platform.preStartupHook(); 100 101 101 Main.logLevel = 3;102 Logging.setLogLevel(Logging.LEVEL_INFO); 102 103 Main.pref.init(false); 103 104 String url = Main.pref.get("osm-server.url"); 104 105 if (url == null || url.isEmpty() || isProductionApiUrl(url)) { -
test/unit/org/openstreetmap/josm/MainTest.java
diff --git a/test/unit/org/openstreetmap/josm/MainTest.java b/test/unit/org/openstreetmap/josm/MainTest.java index 2b4bcb3..e9336bd 100644
a b 2 2 package org.openstreetmap.josm; 3 3 4 4 import static org.junit.Assert.assertEquals; 5 import static org.junit.Assert.assertFalse;6 5 import static org.junit.Assert.assertNull; 7 6 import static org.junit.Assert.assertTrue; 8 7 … … import java.util.Collection; 11 10 import org.junit.BeforeClass; 12 11 import org.junit.Test; 13 12 import org.openstreetmap.josm.Main.DownloadParamType; 14 import org.openstreetmap.josm.gui.MainApplication;15 13 16 14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 17 15 … … public class MainTest { 42 40 } 43 41 44 42 /** 45 * Unit test of {@code Main#preConstructorInit}.46 */47 @Test48 public void testPreConstructorInit() {49 Main.preConstructorInit(MainApplication.buildCommandLineArgumentMap(new String[0]));50 Main.preConstructorInit(MainApplication.buildCommandLineArgumentMap(new String[]{"--geometry=400x300+10+5", "--no-maximize"}));51 //assertEquals(new WindowGeometry(new Point(10, 5), new Dimension(400, 300)), Main.geometry); // FIXME see #1292752 }53 54 /**55 43 * Unit tests on log messages. 56 44 */ 57 45 @Test … … public class MainTest { 78 66 assertTrue(warnings.contains("W: Warning message on one line")); 79 67 assertTrue(warnings.contains("W: First line of warning message on several lines")); 80 68 81 int defaultLevel = Main.logLevel;82 83 // Check levels84 Main.logLevel = 5;85 assertTrue(Main.isTraceEnabled());86 assertTrue(Main.isDebugEnabled());87 88 Main.logLevel = 4;89 assertFalse(Main.isTraceEnabled());90 assertTrue(Main.isDebugEnabled());91 92 Main.logLevel = 3;93 assertFalse(Main.isTraceEnabled());94 assertFalse(Main.isDebugEnabled());95 96 Main.logLevel = defaultLevel;97 69 } 98 70 } -
test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
diff --git a/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java b/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java index 49b6536..3d7558a 100644
a b import org.openstreetmap.josm.io.OsmApi; 19 19 import org.openstreetmap.josm.io.OsmApiInitializationException; 20 20 import org.openstreetmap.josm.io.OsmTransferCanceledException; 21 21 import org.openstreetmap.josm.tools.I18n; 22 import org.openstreetmap.josm.tools.Logging; 22 23 import org.openstreetmap.josm.tools.MemoryManagerTest; 23 24 import org.openstreetmap.josm.tools.date.DateUtils; 24 25 … … public class JOSMTestRules implements TestRule { 181 182 // All tests use the same timezone. 182 183 TimeZone.setDefault(DateUtils.UTC); 183 184 // Set log level to info 184 Main.logLevel = 3;185 Logging.setLogLevel(Logging.LEVEL_INFO); 185 186 186 187 // Set up i18n 187 188 if (i18n != null) { -
new file test/unit/org/openstreetmap/josm/tools/LoggingTest.java
diff --git a/test/unit/org/openstreetmap/josm/tools/LoggingTest.java b/test/unit/org/openstreetmap/josm/tools/LoggingTest.java new file mode 100644 index 0000000..58bd31c
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import static org.junit.Assert.assertArrayEquals; 5 import static org.junit.Assert.assertEquals; 6 import static org.junit.Assert.assertFalse; 7 import static org.junit.Assert.assertNull; 8 import static org.junit.Assert.assertTrue; 9 10 import java.io.IOException; 11 import java.util.function.Consumer; 12 import java.util.logging.Handler; 13 import java.util.logging.Level; 14 import java.util.logging.LogRecord; 15 16 import org.junit.After; 17 import org.junit.Before; 18 import org.junit.Test; 19 20 /** 21 * @author michael 22 * 23 */ 24 public class LoggingTest { 25 26 private LogRecord captured; 27 private final Handler handler = new Handler() { 28 29 @Override 30 public void publish(LogRecord record) { 31 captured = record; 32 } 33 34 @Override 35 public void flush() { 36 } 37 38 @Override 39 public void close() throws SecurityException { 40 } 41 }; 42 43 /** 44 * @throws java.lang.Exception 45 */ 46 @Before 47 public void setUp() throws Exception { 48 captured = null; 49 Logging.getLogger().addHandler(handler); 50 } 51 52 /** 53 * @throws java.lang.Exception 54 */ 55 @After 56 public void tearDown() throws Exception { 57 Logging.getLogger().removeHandler(handler); 58 } 59 60 /** 61 * Test method for {@link org.openstreetmap.josm.tools.Logging#setLogLevel(java.util.logging.Level)}. 62 */ 63 @Test 64 public void testSetLogLevel() { 65 Logging.setLogLevel(Logging.LEVEL_DEBUG); 66 assertEquals(Logging.LEVEL_DEBUG, Logging.getLogger().getLevel()); 67 Logging.setLogLevel(Logging.LEVEL_WARN); 68 assertEquals(Logging.LEVEL_WARN, Logging.getLogger().getLevel()); 69 } 70 71 private void testLogCaptured(Level level, String expected, Runnable printMessage) { 72 testLogCaptured(level, result -> assertEquals(expected, result), printMessage); 73 } 74 75 private void testLogCaptured(Level level, Consumer<String> expectedTester, Runnable printMessage) { 76 Logging.setLogLevel(level); 77 captured = null; 78 printMessage.run(); 79 80 expectedTester.accept(captured.getMessage()); 81 assertEquals(level, captured.getLevel()); 82 83 captured = null; 84 Logging.setLogLevel(Level.OFF); 85 printMessage.run(); 86 assertNull(captured); 87 } 88 89 /** 90 * Test method for {@link org.openstreetmap.josm.tools.Logging#error(java.lang.String)}. 91 */ 92 @Test 93 public void testErrorString() { 94 testLogCaptured(Logging.LEVEL_ERROR, "test", () -> Logging.error("test")); 95 } 96 97 /** 98 * Test method for {@link org.openstreetmap.josm.tools.Logging#error(java.lang.String, java.lang.Object[])}. 99 */ 100 @Test 101 public void testErrorStringObjectArray() { 102 testLogCaptured(Logging.LEVEL_ERROR, "test x 1", () -> Logging.error("test {0} {1}", "x", 1)); 103 } 104 105 /** 106 * Test method for {@link org.openstreetmap.josm.tools.Logging#warn(java.lang.String)}. 107 */ 108 @Test 109 public void testWarnString() { 110 testLogCaptured(Logging.LEVEL_WARN, "test", () -> Logging.warn("test")); 111 } 112 113 /** 114 * Test method for {@link org.openstreetmap.josm.tools.Logging#warn(java.lang.String, java.lang.Object[])}. 115 */ 116 @Test 117 public void testWarnStringObjectArray() { 118 testLogCaptured(Logging.LEVEL_WARN, "test x 1", () -> Logging.warn("test {0} {1}", "x", 1)); 119 } 120 121 /** 122 * Test method for {@link org.openstreetmap.josm.tools.Logging#info(java.lang.String)}. 123 */ 124 @Test 125 public void testInfoString() { 126 testLogCaptured(Logging.LEVEL_INFO, "test", () -> Logging.info("test")); 127 } 128 129 /** 130 * Test method for {@link org.openstreetmap.josm.tools.Logging#info(java.lang.String, java.lang.Object[])}. 131 */ 132 @Test 133 public void testInfoStringObjectArray() { 134 testLogCaptured(Logging.LEVEL_INFO, "test x 1", () -> Logging.info("test {0} {1}", "x", 1)); 135 } 136 137 /** 138 * Test method for {@link org.openstreetmap.josm.tools.Logging#debug(java.lang.String)}. 139 */ 140 @Test 141 public void testDebugString() { 142 testLogCaptured(Logging.LEVEL_DEBUG, "test", () -> Logging.debug("test")); 143 } 144 145 /** 146 * Test method for {@link org.openstreetmap.josm.tools.Logging#debug(java.lang.String, java.lang.Object[])}. 147 */ 148 @Test 149 public void testDebugStringObjectArray() { 150 testLogCaptured(Logging.LEVEL_DEBUG, "test x 1", () -> Logging.debug("test {0} {1}", "x", 1)); 151 } 152 153 /** 154 * Test method for {@link org.openstreetmap.josm.tools.Logging#trace(java.lang.String)}. 155 */ 156 @Test 157 public void testTraceString() { 158 testLogCaptured(Logging.LEVEL_TRACE, "test", () -> Logging.trace("test")); 159 } 160 161 /** 162 * Test method for {@link org.openstreetmap.josm.tools.Logging#trace(java.lang.String, java.lang.Object[])}. 163 */ 164 @Test 165 public void testTraceStringObjectArray() { 166 testLogCaptured(Logging.LEVEL_TRACE, "test x 1", () -> Logging.trace("test {0} {1}", "x", 1)); 167 } 168 169 /** 170 * Test method for {@link org.openstreetmap.josm.tools.Logging#log(java.util.logging.Level, java.lang.Throwable)}. 171 */ 172 @Test 173 public void testLogLevelThrowable() { 174 testLogCaptured(Logging.LEVEL_ERROR, "java.io.IOException: x", () -> Logging.log(Logging.LEVEL_ERROR, new IOException("x"))); 175 176 testLogCaptured(Logging.LEVEL_TRACE, "java.io.IOException: x", () -> Logging.log(Logging.LEVEL_TRACE, new IOException("x"))); 177 } 178 179 /** 180 * Test method for {@link org.openstreetmap.josm.tools.Logging#log(java.util.logging.Level, java.lang.String, java.lang.Throwable)}. 181 */ 182 @Test 183 public void testLogLevelStringThrowable() { 184 testLogCaptured(Logging.LEVEL_ERROR, "y: java.io.IOException: x", () -> Logging.log(Logging.LEVEL_ERROR, "y", new IOException("x"))); 185 186 testLogCaptured(Logging.LEVEL_TRACE, "y: java.io.IOException: x", () -> Logging.log(Logging.LEVEL_TRACE, "y", new IOException("x"))); 187 } 188 189 /** 190 * Test method for {@link org.openstreetmap.josm.tools.Logging#logWithStackTrace(java.util.logging.Level, java.lang.Throwable)}. 191 */ 192 @Test 193 public void testLogWithStackTraceLevelThrowable() { 194 Consumer<String> test = string -> { 195 assertTrue(string.startsWith("java.io.IOException: x")); 196 assertTrue(string.indexOf("testLogWithStackTraceLevelThrowable") >= 0); 197 }; 198 testLogCaptured(Logging.LEVEL_ERROR, test, () -> Logging.logWithStackTrace(Logging.LEVEL_ERROR, new IOException("x"))); 199 testLogCaptured(Logging.LEVEL_TRACE, test, () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, new IOException("x"))); 200 201 testLogCaptured(Logging.LEVEL_TRACE, string -> assertTrue(string.startsWith("java.io.IOException\n")), 202 () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, new IOException())); 203 204 testLogCaptured(Logging.LEVEL_TRACE, string -> assertTrue(string.indexOf("Cause:") >= 0), 205 () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, new IOException(new IOException()))); 206 207 } 208 209 /** 210 * Test method for {@link org.openstreetmap.josm.tools.Logging#logWithStackTrace(java.util.logging.Level, java.lang.String, java.lang.Throwable)}. 211 */ 212 @Test 213 public void testLogWithStackTraceLevelStringThrowable() { 214 Consumer<String> test = string -> { 215 assertTrue(string.startsWith("y: java.io.IOException: x")); 216 assertTrue(string.indexOf("testLogWithStackTraceLevelStringThrowable") > 0); 217 }; 218 testLogCaptured(Logging.LEVEL_ERROR, test, () -> Logging.logWithStackTrace(Logging.LEVEL_ERROR, "y", new IOException("x"))); 219 testLogCaptured(Logging.LEVEL_TRACE, test, () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, "y", new IOException("x"))); 220 } 221 222 /** 223 * Test method for {@link org.openstreetmap.josm.tools.Logging#isLoggingEnabled(java.util.logging.Level)}. 224 */ 225 @Test 226 public void testIsLoggingEnabled() { 227 Logging.setLogLevel(Logging.LEVEL_ERROR); 228 assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_ERROR)); 229 assertFalse(Logging.isLoggingEnabled(Logging.LEVEL_INFO)); 230 assertFalse(Logging.isLoggingEnabled(Logging.LEVEL_TRACE)); 231 Logging.setLogLevel(Logging.LEVEL_INFO); 232 assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_ERROR)); 233 assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_INFO)); 234 assertFalse(Logging.isLoggingEnabled(Logging.LEVEL_TRACE)); 235 Logging.setLogLevel(Logging.LEVEL_TRACE); 236 assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_ERROR)); 237 assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_INFO)); 238 assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_TRACE)); 239 } 240 241 /** 242 * Test method for {@link org.openstreetmap.josm.tools.Logging#clearLastErrorAndWarnings()}. 243 */ 244 @Test 245 public void testClearLastErrorAndWarnings() { 246 Logging.setLogLevel(Logging.LEVEL_WARN); 247 Logging.clearLastErrorAndWarnings(); 248 Logging.error("x"); 249 assertFalse(Logging.getLastErrorAndWarnings().isEmpty()); 250 assertFalse(Logging.getLastErrorAndWarnings().isEmpty()); 251 Logging.clearLastErrorAndWarnings(); 252 assertTrue(Logging.getLastErrorAndWarnings().isEmpty()); 253 } 254 255 /** 256 * Test method for {@link org.openstreetmap.josm.tools.Logging#getLastErrorAndWarnings()}. 257 */ 258 @Test 259 public void testGetLastErrorAndWarnings() { 260 Logging.setLogLevel(Logging.LEVEL_WARN); 261 Logging.clearLastErrorAndWarnings(); 262 Logging.warn("x"); 263 264 assertEquals(1, Logging.getLastErrorAndWarnings().size()); 265 assertEquals("W: x", Logging.getLastErrorAndWarnings().get(0)); 266 267 Logging.setLogLevel(Logging.LEVEL_ERROR); 268 Logging.warn("x"); 269 270 assertEquals(1, Logging.getLastErrorAndWarnings().size()); 271 272 Logging.error("y\nz"); 273 274 assertEquals(2, Logging.getLastErrorAndWarnings().size()); 275 assertArrayEquals(new Object[] {"W: x", "E: y"}, Logging.getLastErrorAndWarnings().toArray()); 276 277 // limit somewhere reasonable 278 for (int i = 3; i < 6; i++) { 279 Logging.error("x"); 280 assertEquals(i, Logging.getLastErrorAndWarnings().size()); 281 } 282 for (int i = 2; i < 100; i++) { 283 Logging.error("x"); 284 } 285 assertTrue(Logging.getLastErrorAndWarnings().size() < 101); 286 } 287 288 }