001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools.bugreport;
003
004/**
005 * This class contains utility methods to create and handle a bug report.
006 * <p>
007 * It allows you to configure the format and request to send the bug report.
008 * <p>
009 * It also contains the main entry point for all components to use the bug report system: Call {@link #intercept(Throwable)} to start handling an
010 * exception.
011 * <h1> Handling Exceptions </h1>
012 * In your code, you should add try...catch blocks for any runtime exceptions that might happen. It is fine to catch throwable there.
013 * <p>
014 * You should then add some debug information there. This can be the OSM ids that caused the error, information on the data you were working on
015 * or other local variables. Make sure that no excpetions may occur while computing the values. It is best to send plain local variables to
016 * put(...). Then simply throw the throwable you got from the bug report. The global exception handler will do the rest.
017 * <pre>
018 * int id = ...;
019 * String tag = "...";
020 * try {
021 *   ... your code ...
022 * } catch (Throwable t) {
023 *   throw BugReport.intercept(t).put("id", id).put("tag", tag);
024 * }
025 * </pre>
026 *
027 * Instead of re-throwing, you can call {@link ReportedException#warn()}. This will display a warning to the user and allow it to either report
028 * the execption or ignore it.
029 *
030 * @author Michael Zangl
031 * @since 10285
032 */
033public class BugReport {
034    /**
035     * Create a new bug report
036     * @param e The {@link ReportedException} to use. No more data should be added after creating the report.
037     */
038    public BugReport(ReportedException e) {
039        // TODO: Use this class to create the bug report.
040    }
041
042    /**
043     * This should be called whenever you want to add more information to a given exception.
044     * @param t The throwable that was thrown.
045     * @return A {@link ReportedException} to which you can add additional information.
046     */
047    public static ReportedException intercept(Throwable t) {
048        ReportedException e;
049        if (t instanceof ReportedException) {
050            e = (ReportedException) t;
051        } else {
052            e = new ReportedException(t);
053        }
054        e.startSection(getCallingMethod(2));
055        return e;
056    }
057
058    /**
059     * Find the method that called us.
060     *
061     * @param offset
062     *            How many methods to look back in the stack trace. 1 gives the method calling this method, 0 gives you getCallingMethod().
063     * @return The method name.
064     */
065    static String getCallingMethod(int offset) {
066        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
067        String className = BugReport.class.getName();
068        for (int i = 0; i < stackTrace.length - offset; i++) {
069            StackTraceElement element = stackTrace[i];
070            if (className.equals(element.getClassName()) && "getCallingMethod".equals(element.getMethodName())) {
071                StackTraceElement toReturn = stackTrace[i + offset];
072                return toReturn.getClassName().replaceFirst(".*\\.", "") + '#' + toReturn.getMethodName();
073            }
074        }
075        return "?";
076    }
077}