Ticket #13318: patch-fix-13318.patch
File patch-fix-13318.patch, 58.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 a2ac9c0..c9a3b56 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 { 490 452 * @since 6642 491 453 */ 492 454 public static String getErrorMessage(Throwable t) { 493 if (t == null) { 494 return null; 495 } 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(); 455 return Logging.getErrorMessage(t); 506 456 } 507 457 508 458 /** … … public abstract class Main { 585 535 isOpenjdk = System.getProperty("java.vm.name").toUpperCase(Locale.ENGLISH).indexOf("OPENJDK") != -1; 586 536 fileWatcher.start(); 587 537 588 new InitializationTask(tr("Executing platform startup hook")) { 589 @Override 590 public void initialize() { 591 platform.startupHook(); 592 } 593 }.call(); 538 new InitializationTask(tr("Executing platform startup hook"), platform::startupHook).call(); 594 539 595 new InitializationTask(tr("Building main menu")) { 596 597 @Override 598 public void initialize() { 599 initializeMainWindow(); 600 } 601 }.call(); 540 new InitializationTask(tr("Building main menu"), this::initializeMainWindow).call(); 602 541 603 542 undoRedo.addCommandQueueListener(redoUndoListener); 604 543 … … public abstract class Main { 611 550 // contains several initialization tasks to be executed (in parallel) by a ExecutorService 612 551 List<Callable<Void>> tasks = new ArrayList<>(); 613 552 614 tasks.add(new InitializationTask(tr("Initializing OSM API")) { 615 616 @Override 617 public void initialize() { 553 tasks.add(new InitializationTask(tr("Initializing OSM API"), () -> { 618 554 // We try to establish an API connection early, so that any API 619 555 // capabilities are already known to the editor instance. However 620 556 // if it goes wrong that's not critical at this stage. … … public abstract class Main { 623 559 } catch (OsmTransferCanceledException | OsmApiInitializationException e) { 624 560 Main.warn(getErrorMessage(Utils.getRootCause(e))); 625 561 } 626 } 627 }); 628 629 tasks.add(new InitializationTask(tr("Initializing validator")) { 562 })); 630 563 631 @Override 632 public void initialize() { 633 OsmValidator.initialize(); 634 } 635 }); 564 tasks.add(new InitializationTask(tr("Initializing validator"), OsmValidator::initialize)); 636 565 637 tasks.add(new InitializationTask(tr("Initializing presets") ) {566 tasks.add(new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize)); 638 567 639 @Override 640 public void initialize() { 641 TaggingPresets.initialize(); 642 } 643 }); 568 tasks.add(new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize)); 644 569 645 tasks.add(new InitializationTask(tr("Initializing map styles")) { 646 647 @Override 648 public void initialize() { 649 MapPaintPreference.initialize(); 650 } 651 }); 652 653 tasks.add(new InitializationTask(tr("Loading imagery preferences")) { 654 655 @Override 656 public void initialize() { 657 ImageryPreference.initialize(); 658 } 659 }); 570 tasks.add(new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize)); 660 571 661 572 try { 662 finalExecutorService service = Executors.newFixedThreadPool(573 ExecutorService service = Executors.newFixedThreadPool( 663 574 Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY)); 664 575 for (Future<Void> i : service.invokeAll(tasks)) { 665 576 i.get(); … … public abstract class Main { 672 583 // hooks for the jmapviewer component 673 584 FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl); 674 585 FeatureAdapter.registerTranslationAdapter(I18n.getTranslationAdapter()); 675 FeatureAdapter.registerLoggingAdapter(name -> { 676 Logger logger = Logger.getAnonymousLogger(); 677 logger.setUseParentHandlers(false); 678 logger.setLevel(Level.ALL); 679 if (logger.getHandlers().length == 0) { 680 logger.addHandler(new Handler() { 681 @Override 682 public void publish(LogRecord record) { 683 String msg = MessageFormat.format(record.getMessage(), record.getParameters()); 684 if (record.getLevel().intValue() >= Level.SEVERE.intValue()) { 685 Main.error(msg); 686 } else if (record.getLevel().intValue() >= Level.WARNING.intValue()) { 687 Main.warn(msg); 688 } else if (record.getLevel().intValue() >= Level.INFO.intValue()) { 689 Main.info(msg); 690 } else if (record.getLevel().intValue() >= Level.FINE.intValue()) { 691 Main.debug(msg); 692 } else { 693 Main.trace(msg); 694 } 695 } 696 697 @Override 698 public void flush() { 699 // Do nothing 700 } 701 702 @Override 703 public void close() { 704 // Do nothing 705 } 706 }); 707 } 708 return logger; 709 }); 586 FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger()); 710 587 711 new InitializationTask(tr("Updating user interface")) { 712 713 @Override 714 public void initialize() { 715 toolbar.refreshToolbarControl(); 716 toolbar.control.updateUI(); 717 contentPanePrivate.updateUI(); 718 } 719 }.call(); 588 new InitializationTask(tr("Updating user interface"), () -> { 589 toolbar.refreshToolbarControl(); 590 toolbar.control.updateUI(); 591 contentPanePrivate.updateUI(); 592 }).call(); 720 593 } 721 594 722 595 /** … … public abstract class Main { 727 600 // can be implementd by subclasses 728 601 } 729 602 730 private abstractstatic class InitializationTask implements Callable<Void> {603 private static class InitializationTask implements Callable<Void> { 731 604 732 605 private final String name; 606 private Runnable task; 733 607 734 protected InitializationTask(String name ) {608 protected InitializationTask(String name, Runnable task) { 735 609 this.name = name; 610 this.task = task; 736 611 } 737 612 738 public abstract void initialize();739 740 613 @Override 741 614 public Void call() { 742 615 Object status = null; 743 616 if (initListener != null) { 744 617 status = initListener.updateStatus(name); 745 618 } 746 initialize();619 task.run(); 747 620 if (initListener != null) { 748 621 initListener.finish(status); 749 622 } … … public abstract class Main { 965 838 * Should be called before the main constructor to setup some parameter stuff 966 839 * @param args The parsed argument list. 967 840 */ 968 public static void preConstructorInit( Map<Option, Collection<String>>args) {841 public static void preConstructorInit(ProgramArguments args) { 969 842 ProjectionPreference.setProjection(); 970 843 971 844 String defaultlaf = platform.getDefaultStyle(); … … public abstract class Main { 1032 905 } 1033 906 } 1034 907 1035 protected static void postConstructorProcessCmdLine(Map<Option, Collection<String>> args) { 1036 if (args.containsKey(Option.DOWNLOAD)) { 1037 List<File> fileList = new ArrayList<>(); 1038 for (String s : args.get(Option.DOWNLOAD)) { 1039 DownloadParamType.paramType(s).download(s, fileList); 1040 } 1041 if (!fileList.isEmpty()) { 1042 OpenFileAction.openFiles(fileList, true); 1043 } 908 protected static void postConstructorProcessCmdLine(ProgramArguments args) { 909 List<File> fileList = new ArrayList<>(); 910 for (String s : args.get(Option.DOWNLOAD)) { 911 DownloadParamType.paramType(s).download(s, fileList); 1044 912 } 1045 if (args.containsKey(Option.DOWNLOADGPS)) { 1046 for (String s : args.get(Option.DOWNLOADGPS)) { 1047 DownloadParamType.paramType(s).downloadGps(s); 1048 } 913 if (!fileList.isEmpty()) { 914 OpenFileAction.openFiles(fileList, true); 1049 915 } 1050 if (args.containsKey(Option.SELECTION)) { 1051 for (String s : args.get(Option.SELECTION)) { 1052 SearchAction.search(s, SearchAction.SearchMode.add); 1053 } 916 for (String s : args.get(Option.DOWNLOADGPS)) { 917 DownloadParamType.paramType(s).downloadGps(s); 918 } 919 for (String s : args.get(Option.SELECTION)) { 920 SearchAction.search(s, SearchAction.SearchMode.add); 1054 921 } 1055 922 } 1056 923 … … public abstract class Main { 1503 1370 public static void setup() { 1504 1371 if (!windowSwitchListeners.isEmpty()) { 1505 1372 for (Window w : Window.getWindows()) { 1506 if (w.isShowing()) { 1507 if (!Arrays.asList(w.getWindowListeners()).contains(getInstance())) { 1508 w.addWindowListener(getInstance()); 1509 } 1373 if (w.isShowing() && !Arrays.asList(w.getWindowListeners()).contains(getInstance())) { 1374 w.addWindowListener(getInstance()); 1510 1375 } 1511 1376 } 1512 1377 } -
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 b65e16c..c730abf 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); 187 Level logLevel = args.getLogLevel(); 188 Logging.setLogLevel(logLevel); 189 Main.info(tr("Log level is at ", logLevel)); 311 190 312 if (languageGiven) { 313 I18n.set(args.get(Option.LANGUAGE).iterator().next()); 314 } 191 Optional<String> language = args.getSingle(Option.LANGUAGE); 192 I18n.set(language.orElse(null)); 315 193 316 194 initApplicationPreferences(); 317 195 … … public class MainApplication extends Main { 339 217 340 218 Main.COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray)); 341 219 342 if (args. containsKey(Option.VERSION)) {220 if (args.showVersion()) { 343 221 System.out.println(Version.getInstance().getAgentString()); 344 222 System.exit(0); 345 } 346 347 if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) { 348 // Enable JOSM debug level 349 logLevel = 4; 350 Main.info(tr("Printing debugging messages to console")); 223 } else if (args.showHelp()) { 224 showHelp(); 225 System.exit(0); 351 226 } 352 227 353 228 boolean skipLoadingPlugins = false; 354 if (args. containsKey(Option.SKIP_PLUGINS)) {229 if (args.hasOption(Option.SKIP_PLUGINS)) { 355 230 skipLoadingPlugins = true; 356 231 Main.info(tr("Plugin loading skipped")); 357 232 } 358 233 359 if (args.containsKey(Option.TRACE)) { 360 // Enable JOSM debug level 361 logLevel = 5; 234 if (Logging.isLoggingEnabled(Logging.LEVEL_TRACE)) { 362 235 // Enable debug in OAuth signpost via system preference, but only at trace level 363 236 Utils.updateSystemProperty("debug", "true"); 364 237 Main.info(tr("Enabled detailed debug level (trace)")); 365 238 } 366 239 367 Main.pref.init(args. containsKey(Option.RESET_PREFERENCES));240 Main.pref.init(args.hasOption(Option.RESET_PREFERENCES)); 368 241 369 if (args.containsKey(Option.SET)) { 370 for (String i : args.get(Option.SET)) { 371 String[] kv = i.split("=", 2); 372 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]); 373 } 374 } 242 args.getPreferencesToSet().forEach(Main.pref::put); 375 243 376 if (!language Given) {244 if (!language.isPresent()) { 377 245 I18n.set(Main.pref.get("language", null)); 378 246 } 379 247 Main.pref.updateSystemProperties(); … … public class MainApplication extends Main { 381 249 checkIPv6(); 382 250 383 251 // asking for help? show help and exit 384 if (args. containsKey(Option.HELP)) {252 if (args.hasOption(Option.HELP)) { 385 253 showHelp(); 386 254 System.exit(0); 387 255 } … … public class MainApplication extends Main { 395 263 I18n.setupLanguageFonts(); 396 264 397 265 WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry", 398 args. containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null,399 !args. containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));266 args.getSingle(Option.GEOMETRY).orElse(null), 267 !args.hasOption(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false)); 400 268 final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry); 401 269 Main.parent = mainFrame; 402 270 403 if (args. containsKey(Option.LOAD_PREFERENCES)) {271 if (args.hasOption(Option.LOAD_PREFERENCES)) { 404 272 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 405 273 for (String i : args.get(Option.LOAD_PREFERENCES)) { 406 274 info("Reading preferences from " + i); 407 275 try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) { 408 276 config.openAndReadXML(is); 409 277 } catch (IOException ex) { 410 throw new RuntimeException(ex);278 throw BugReport.intercept(ex).put("file", i); 411 279 } 412 280 } 413 281 } … … public class MainApplication extends Main { 470 338 Main.MasterWindowListener.setup(); 471 339 472 340 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 473 if ((!args. containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {341 if ((!args.hasOption(Option.NO_MAXIMIZE) && maximized) || args.hasOption(Option.MAXIMIZE)) { 474 342 mainFrame.setMaximized(true); 475 343 } 476 344 if (main.menu.fullscreenToggleAction != null) { … … public class MainApplication extends Main { 528 396 toolbar.refreshToolbarControl(); 529 397 } 530 398 531 private static void processOffline( Map<Option, Collection<String>>args) {532 if (args.containsKey(Option.OFFLINE)) {533 for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) {399 private static void processOffline(ProgramArguments args) { 400 for (String offlineNames : args.get(Option.OFFLINE)) { 401 for (String s : offlineNames.split(",")) { 534 402 try { 535 403 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 536 404 } catch (IllegalArgumentException e) { … … public class MainApplication extends Main { 540 408 return; 541 409 } 542 410 } 543 Set<OnlineResource> offline = Main.getOfflineResources();544 if (!offline.isEmpty()) {545 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",546 "JOSM is running in offline mode. These resourceswill not be available: {0}",547 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));548 }411 } 412 Set<OnlineResource> offline = Main.getOfflineResources(); 413 if (!offline.isEmpty()) { 414 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 415 "JOSM is running in offline mode. These resources will not be available: {0}", 416 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 549 417 } 550 418 } 551 419 … … public class MainApplication extends Main { 600 468 601 469 private static class GuiFinalizationWorker implements Runnable { 602 470 603 private final Map<Option, Collection<String>>args;471 private final ProgramArguments args; 604 472 private final DefaultProxySelector proxySelector; 605 473 606 GuiFinalizationWorker( Map<Option, Collection<String>>args, DefaultProxySelector proxySelector) {474 GuiFinalizationWorker(ProgramArguments args, DefaultProxySelector proxySelector) { 607 475 this.args = args; 608 476 this.proxySelector = proxySelector; 609 477 } -
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..db6855f
- + 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 sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause)); 282 } 283 return sb.toString(); 284 } 285 286 /** 287 * Clear the list of last warnings 288 */ 289 public static void clearLastErrorAndWarnings() { 290 WARNINGS.clear(); 291 } 292 293 /** 294 * Get the last error and warning messages in the order in which they were received. 295 * @return The last errors and warnings. 296 */ 297 public static List<String> getLastErrorAndWarnings() { 298 return WARNINGS.getMessages(); 299 } 300 301 /** 302 * Provides direct access to the logger used. Use of methods like {@link #warn(String)} is prefered. 303 * @return The logger 304 */ 305 public static Logger getLogger() { 306 return LOGGER; 307 } 308 309 private static class RememberWarningHandler extends Handler { 310 private final String[] log = new String[10]; 311 private int messagesLogged; 312 313 RememberWarningHandler() { 314 setLevel(LEVEL_WARN); 315 } 316 317 synchronized void clear() { 318 messagesLogged = 0; 319 Arrays.fill(log, null); 320 } 321 322 @Override 323 public synchronized void publish(LogRecord record) { 324 if (!isLoggable(record)) { 325 return; 326 } 327 328 String msg = getPrefix(record) + record.getMessage(); 329 330 // Only remember first line of message 331 int idx = msg.indexOf('\n'); 332 if (idx > 0) { 333 msg = msg.substring(0, idx); 334 } 335 log[messagesLogged % log.length] = msg; 336 messagesLogged++; 337 } 338 339 private static String getPrefix(LogRecord record) { 340 if (record.getLevel().equals(LEVEL_WARN)) { 341 return "W: "; 342 } else { 343 // worse than warn 344 return "E: "; 345 } 346 } 347 348 synchronized List<String> getMessages() { 349 List<String> logged = Arrays.asList(log); 350 ArrayList<String> res = new ArrayList<>(); 351 int logOffset = messagesLogged % log.length; 352 if (messagesLogged > logOffset) { 353 res.addAll(logged.subList(logOffset, log.length)); 354 } 355 res.addAll(logged.subList(0, logOffset)); 356 return res; 357 } 358 359 @Override 360 public synchronized void flush() { 361 // nothing to do 362 } 363 364 @Override 365 public void close() { 366 // nothing to do 367 } 368 } 369 } -
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 d47586f..426cb59 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 { 180 181 * @return The method name. 181 182 */ 182 183 public static String getCallingMethod(int offset) { 183 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();184 184 String className = BugReport.class.getName(); 185 String methodName = "getCallingMethod"; 186 StackTraceElement found = getCallingMethod(offset, className, methodName::equals); 187 if (found != null) { 188 return found.getClassName().replaceFirst(".*\\.", "") + '#' + found.getMethodName(); 189 } else { 190 return "?"; 191 } 192 } 193 194 /** 195 * Find the method that called the given method on the current stack trace. 196 * @param offset 197 * How many methods to look back in the stack trace. 1 gives the method calling this method, 0 gives you getCallingMethod(). 198 * @param className The name of the class to search for 199 * @param methodName The name of the method to search for 200 * @return The class and method name or null if it is unknown. 201 */ 202 public static StackTraceElement getCallingMethod(int offset, String className, Predicate<String> methodName) { 203 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 185 204 for (int i = 0; i < stackTrace.length - offset; i++) { 186 205 StackTraceElement element = stackTrace[i]; 187 if (className.equals(element.getClassName()) && "getCallingMethod".equals(element.getMethodName())) {206 if (className.equals(element.getClassName()) && methodName.test(element.getMethodName())) { 188 207 StackTraceElement toReturn = stackTrace[i + offset]; 189 return toReturn .getClassName().replaceFirst(".*\\.", "") + '#' + toReturn.getMethodName();208 return toReturn; 190 209 } 191 210 } 192 return "?";211 return null; 193 212 } 194 213 195 214 /** -
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 5e31aa7..7596606 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 { 99 100 Main.platform.preStartupHook(); 100 101 101 102 Main.logLevel = 3; 103 Logging.setLogLevel(Logging.LEVEL_INFO); 102 104 Main.pref.init(false); 103 105 Main.pref.put("osm-server.url", "http://api06.dev.openstreetmap.org/api"); 104 106 I18n.set(Main.pref.get("language", "en")); -
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 634b625..d46b251 100644
a b import java.io.File; 5 5 import java.io.IOException; 6 6 import java.text.MessageFormat; 7 7 import java.util.TimeZone; 8 import java.util.logging.Level; 8 9 9 10 import org.junit.rules.TemporaryFolder; 10 11 import org.junit.rules.TestRule; … … import org.openstreetmap.josm.io.OsmApi; 19 20 import org.openstreetmap.josm.io.OsmApiInitializationException; 20 21 import org.openstreetmap.josm.io.OsmTransferCanceledException; 21 22 import org.openstreetmap.josm.tools.I18n; 23 import org.openstreetmap.josm.tools.Logging; 22 24 import org.openstreetmap.josm.tools.MemoryManagerTest; 23 25 import org.openstreetmap.josm.tools.date.DateUtils; 24 26 … … public class JOSMTestRules implements TestRule { 182 184 TimeZone.setDefault(DateUtils.UTC); 183 185 // Set log level to info 184 186 Main.logLevel = 3; 187 Logging.setLogLevel(Level.INFO); 185 188 186 189 // Set up i18n 187 190 if (i18n != null) {