@Override
 public String visitCapturedType(CapturedType t, Locale locale) {
   if (!allCaptured.contains(t)) {
     allCaptured = allCaptured.append(t);
   }
   return super.visitCapturedType(t, locale);
 }
 /**
  * Format all the subdiagnostics attached to a given diagnostic.
  *
  * @param d diagnostic whose subdiagnostics are to be formatted
  * @param l locale object to be used for i18n
  * @return list of all string representations of the subdiagnostics
  */
 protected List<String> formatSubdiagnostics(JCDiagnostic d, Locale l) {
   List<String> subdiagnostics = List.nil();
   int maxDepth = config.getMultilineLimit(MultilineLimit.DEPTH);
   if (maxDepth == -1 || depth < maxDepth) {
     depth++;
     try {
       int maxCount = config.getMultilineLimit(MultilineLimit.LENGTH);
       int count = 0;
       for (JCDiagnostic d2 : d.getSubdiagnostics()) {
         if (maxCount == -1 || count < maxCount) {
           subdiagnostics = subdiagnostics.append(formatSubdiagnostic(d, d2, l));
           count++;
         } else break;
       }
     } finally {
       depth--;
     }
   }
   return subdiagnostics;
 }
 /**
  * Get the subdiagnostic list
  *
  * @return subdiagnostic list
  */
 public List<JCDiagnostic> getSubdiagnostics() {
   return List.nil();
 }
/**
 * This abstract class provides a basic implementation of the functionalities that should be
 * provided by any formatter used by javac. Among the main features provided by
 * AbstractDiagnosticFormatter are:
 *
 * <ul>
 *   <li>Provides a standard implementation of the visitor-like methods defined in the interface
 *       DiagnisticFormatter. Those implementations are specifically targeting JCDiagnostic objects.
 *   <li>Provides basic support for i18n and a method for executing all locale-dependent conversions
 *   <li>Provides the formatting logic for rendering the arguments of a JCDiagnostic object.
 *       <ul>
 *         <p><b>This is NOT part of any supported API. If you write code that depends on this, you
 *         do so at your own risk. This code and its internal interfaces are subject to change or
 *         deletion without notice.</b>
 */
public abstract class AbstractDiagnosticFormatter implements DiagnosticFormatter<JCDiagnostic> {

  /** JavacMessages object used by this formatter for i18n. */
  protected JavacMessages messages;

  /** Configuration object used by this formatter */
  private SimpleConfiguration config;

  /** Current depth level of the disgnostic being formatted (!= 0 for subdiagnostics) */
  protected int depth = 0;

  /**
   * All captured types that have been encountered during diagnostic formatting. This info is used
   * by the FormatterPrinter in order to print friendly unique ids for captured types
   */
  private List<Type> allCaptured = List.nil();

  /**
   * Initialize an AbstractDiagnosticFormatter by setting its JavacMessages object.
   *
   * @param messages
   */
  protected AbstractDiagnosticFormatter(JavacMessages messages, SimpleConfiguration config) {
    this.messages = messages;
    this.config = config;
  }

  public String formatKind(JCDiagnostic d, Locale l) {
    switch (d.getType()) {
      case FRAGMENT:
        return "";
      case NOTE:
        return localize(l, "compiler.note.note");
      case WARNING:
        return localize(l, "compiler.warn.warning");
      case ERROR:
        return localize(l, "compiler.err.error");
      default:
        throw new AssertionError("Unknown diagnostic type: " + d.getType());
    }
  }

  @Override
  public String format(JCDiagnostic d, Locale locale) {
    allCaptured = List.nil();
    return formatDiagnostic(d, locale);
  }

  protected abstract String formatDiagnostic(JCDiagnostic d, Locale locale);

  public String formatPosition(JCDiagnostic d, PositionKind pk, Locale l) {
    Assert.check(d.getPosition() != Position.NOPOS);
    return String.valueOf(getPosition(d, pk));
  }
  // where
  private long getPosition(JCDiagnostic d, PositionKind pk) {
    switch (pk) {
      case START:
        return d.getIntStartPosition();
      case END:
        return d.getIntEndPosition();
      case LINE:
        return d.getLineNumber();
      case COLUMN:
        return d.getColumnNumber();
      case OFFSET:
        return d.getIntPosition();
      default:
        throw new AssertionError("Unknown diagnostic position: " + pk);
    }
  }

  public String formatSource(JCDiagnostic d, boolean fullname, Locale l) {
    JavaFileObject fo = d.getSource();
    if (fo == null) throw new IllegalArgumentException(); // d should have source set
    if (fullname) return fo.getName();
    else if (fo instanceof BaseFileObject) return ((BaseFileObject) fo).getShortName();
    else return BaseFileObject.getSimpleName(fo);
  }

  /**
   * Format the arguments of a given diagnostic.
   *
   * @param d diagnostic whose arguments are to be formatted
   * @param l locale object to be used for i18n
   * @return a Collection whose elements are the formatted arguments of the diagnostic
   */
  protected Collection<String> formatArguments(JCDiagnostic d, Locale l) {
    ListBuffer<String> buf = new ListBuffer<String>();
    for (Object o : d.getArgs()) {
      buf.append(formatArgument(d, o, l));
    }
    return buf.toList();
  }

  /**
   * Format a single argument of a given diagnostic.
   *
   * @param d diagnostic whose argument is to be formatted
   * @param arg argument to be formatted
   * @param l locale object to be used for i18n
   * @return string representation of the diagnostic argument
   */
  protected String formatArgument(JCDiagnostic d, Object arg, Locale l) {
    if (arg instanceof JCDiagnostic) {
      String s = null;
      depth++;
      try {
        s = formatMessage((JCDiagnostic) arg, l);
      } finally {
        depth--;
      }
      return s;
    } else if (arg instanceof JCExpression) {
      return expr2String((JCExpression) arg);
    } else if (arg instanceof Iterable<?>) {
      return formatIterable(d, (Iterable<?>) arg, l);
    } else if (arg instanceof Type) {
      return printer.visit((Type) arg, l);
    } else if (arg instanceof Symbol) {
      return printer.visit((Symbol) arg, l);
    } else if (arg instanceof JavaFileObject) {
      return ((JavaFileObject) arg).getName();
    } else if (arg instanceof Profile) {
      return ((Profile) arg).name;
    } else if (arg instanceof Formattable) {
      return ((Formattable) arg).toString(l, messages);
    } else {
      return String.valueOf(arg);
    }
  }
  // where
  private String expr2String(JCExpression tree) {
    switch (tree.getTag()) {
      case PARENS:
        return expr2String(((JCParens) tree).expr);
      case LAMBDA:
      case REFERENCE:
      case CONDEXPR:
        return Pretty.toSimpleString(tree);
      default:
        Assert.error("unexpected tree kind " + tree.getKind());
        return null;
    }
  }

  /**
   * Format an iterable argument of a given diagnostic.
   *
   * @param d diagnostic whose argument is to be formatted
   * @param it iterable argument to be formatted
   * @param l locale object to be used for i18n
   * @return string representation of the diagnostic iterable argument
   */
  protected String formatIterable(JCDiagnostic d, Iterable<?> it, Locale l) {
    StringBuilder sbuf = new StringBuilder();
    String sep = "";
    for (Object o : it) {
      sbuf.append(sep);
      sbuf.append(formatArgument(d, o, l));
      sep = ",";
    }
    return sbuf.toString();
  }

  /**
   * Format all the subdiagnostics attached to a given diagnostic.
   *
   * @param d diagnostic whose subdiagnostics are to be formatted
   * @param l locale object to be used for i18n
   * @return list of all string representations of the subdiagnostics
   */
  protected List<String> formatSubdiagnostics(JCDiagnostic d, Locale l) {
    List<String> subdiagnostics = List.nil();
    int maxDepth = config.getMultilineLimit(MultilineLimit.DEPTH);
    if (maxDepth == -1 || depth < maxDepth) {
      depth++;
      try {
        int maxCount = config.getMultilineLimit(MultilineLimit.LENGTH);
        int count = 0;
        for (JCDiagnostic d2 : d.getSubdiagnostics()) {
          if (maxCount == -1 || count < maxCount) {
            subdiagnostics = subdiagnostics.append(formatSubdiagnostic(d, d2, l));
            count++;
          } else break;
        }
      } finally {
        depth--;
      }
    }
    return subdiagnostics;
  }

  /**
   * Format a subdiagnostics attached to a given diagnostic.
   *
   * @param parent multiline diagnostic whose subdiagnostics is to be formatted
   * @param sub subdiagnostic to be formatted
   * @param l locale object to be used for i18n
   * @return string representation of the subdiagnostics
   */
  protected String formatSubdiagnostic(JCDiagnostic parent, JCDiagnostic sub, Locale l) {
    return formatMessage(sub, l);
  }

  /**
   * Format the faulty source code line and point to the error.
   *
   * @param d The diagnostic for which the error line should be printed
   */
  protected String formatSourceLine(JCDiagnostic d, int nSpaces) {
    StringBuilder buf = new StringBuilder();
    DiagnosticSource source = d.getDiagnosticSource();
    int pos = d.getIntPosition();
    if (d.getIntPosition() == Position.NOPOS) throw new AssertionError();
    String line = (source == null ? null : source.getLine(pos));
    if (line == null) return "";
    buf.append(indent(line, nSpaces));
    int col = source.getColumnNumber(pos, false);
    if (config.isCaretEnabled()) {
      buf.append("\n");
      for (int i = 0; i < col - 1; i++) {
        buf.append((line.charAt(i) == '\t') ? "\t" : " ");
      }
      buf.append(indent("^", nSpaces));
    }
    return buf.toString();
  }

  protected String formatLintCategory(JCDiagnostic d, Locale l) {
    LintCategory lc = d.getLintCategory();
    if (lc == null) return "";
    return localize(l, "compiler.warn.lintOption", lc.option);
  }

  /**
   * Converts a String into a locale-dependent representation accordingly to a given locale.
   *
   * @param l locale object to be used for i18n
   * @param key locale-independent key used for looking up in a resource file
   * @param args localization arguments
   * @return a locale-dependent string
   */
  protected String localize(Locale l, String key, Object... args) {
    return messages.getLocalizedString(l, key, args);
  }

  public boolean displaySource(JCDiagnostic d) {
    return config.getVisible().contains(DiagnosticPart.SOURCE)
        && d.getType() != FRAGMENT
        && d.getIntPosition() != Position.NOPOS;
  }

  public boolean isRaw() {
    return false;
  }

  /**
   * Creates a string with a given amount of empty spaces. Useful for indenting the text of a
   * diagnostic message.
   *
   * @param nSpaces the amount of spaces to be added to the result string
   * @return the indentation string
   */
  protected String indentString(int nSpaces) {
    String spaces = "                        ";
    if (nSpaces <= spaces.length()) return spaces.substring(0, nSpaces);
    else {
      StringBuilder buf = new StringBuilder();
      for (int i = 0; i < nSpaces; i++) buf.append(" ");
      return buf.toString();
    }
  }

  /**
   * Indent a string by prepending a given amount of empty spaces to each line of the string.
   *
   * @param s the string to be indented
   * @param nSpaces the amount of spaces that should be prepended to each line of the string
   * @return an indented string
   */
  protected String indent(String s, int nSpaces) {
    String indent = indentString(nSpaces);
    StringBuilder buf = new StringBuilder();
    String nl = "";
    for (String line : s.split("\n")) {
      buf.append(nl);
      buf.append(indent + line);
      nl = "\n";
    }
    return buf.toString();
  }

  public SimpleConfiguration getConfiguration() {
    return config;
  }

  public static class SimpleConfiguration implements Configuration {

    protected Map<MultilineLimit, Integer> multilineLimits;
    protected EnumSet<DiagnosticPart> visibleParts;
    protected boolean caretEnabled;

    public SimpleConfiguration(Set<DiagnosticPart> parts) {
      multilineLimits = new HashMap<MultilineLimit, Integer>();
      setVisible(parts);
      setMultilineLimit(MultilineLimit.DEPTH, -1);
      setMultilineLimit(MultilineLimit.LENGTH, -1);
      setCaretEnabled(true);
    }

    @SuppressWarnings("fallthrough")
    public SimpleConfiguration(Options options, Set<DiagnosticPart> parts) {
      this(parts);
      String showSource = null;
      if ((showSource = options.get("showSource")) != null) {
        if (showSource.equals("true")) setVisiblePart(DiagnosticPart.SOURCE, true);
        else if (showSource.equals("false")) setVisiblePart(DiagnosticPart.SOURCE, false);
      }
      String diagOpts = options.get("diags");
      if (diagOpts != null) { // override -XDshowSource
        Collection<String> args = Arrays.asList(diagOpts.split(","));
        if (args.contains("short")) {
          setVisiblePart(DiagnosticPart.DETAILS, false);
          setVisiblePart(DiagnosticPart.SUBDIAGNOSTICS, false);
        }
        if (args.contains("source")) setVisiblePart(DiagnosticPart.SOURCE, true);
        if (args.contains("-source")) setVisiblePart(DiagnosticPart.SOURCE, false);
      }
      String multiPolicy = null;
      if ((multiPolicy = options.get("multilinePolicy")) != null) {
        if (multiPolicy.equals("disabled")) setVisiblePart(DiagnosticPart.SUBDIAGNOSTICS, false);
        else if (multiPolicy.startsWith("limit:")) {
          String limitString = multiPolicy.substring("limit:".length());
          String[] limits = limitString.split(":");
          try {
            switch (limits.length) {
              case 2:
                {
                  if (!limits[1].equals("*"))
                    setMultilineLimit(MultilineLimit.DEPTH, Integer.parseInt(limits[1]));
                }
              case 1:
                {
                  if (!limits[0].equals("*"))
                    setMultilineLimit(MultilineLimit.LENGTH, Integer.parseInt(limits[0]));
                }
            }
          } catch (NumberFormatException ex) {
            setMultilineLimit(MultilineLimit.DEPTH, -1);
            setMultilineLimit(MultilineLimit.LENGTH, -1);
          }
        }
      }
      String showCaret = null;
      if (((showCaret = options.get("showCaret")) != null) && showCaret.equals("false"))
        setCaretEnabled(false);
      else setCaretEnabled(true);
    }

    public int getMultilineLimit(MultilineLimit limit) {
      return multilineLimits.get(limit);
    }

    public EnumSet<DiagnosticPart> getVisible() {
      return EnumSet.copyOf(visibleParts);
    }

    public void setMultilineLimit(MultilineLimit limit, int value) {
      multilineLimits.put(limit, value < -1 ? -1 : value);
    }

    public void setVisible(Set<DiagnosticPart> diagParts) {
      visibleParts = EnumSet.copyOf(diagParts);
    }

    public void setVisiblePart(DiagnosticPart diagParts, boolean enabled) {
      if (enabled) visibleParts.add(diagParts);
      else visibleParts.remove(diagParts);
    }

    /**
     * Shows a '^' sign under the source line displayed by the formatter (if applicable).
     *
     * @param caretEnabled if true enables caret
     */
    public void setCaretEnabled(boolean caretEnabled) {
      this.caretEnabled = caretEnabled;
    }

    /**
     * Tells whether the caret display is active or not.
     *
     * @return true if the caret is enabled
     */
    public boolean isCaretEnabled() {
      return caretEnabled;
    }
  }

  public Printer getPrinter() {
    return printer;
  }

  public void setPrinter(Printer printer) {
    this.printer = printer;
  }

  /**
   * An enhanced printer for formatting types/symbols used by AbstractDiagnosticFormatter. Provides
   * alternate numbering of captured types (they are numbered starting from 1 on each new
   * diagnostic, instead of relying on the underlying hashcode() method which generates unstable
   * output). Also detects cycles in wildcard messages (e.g. if the wildcard type referred by a
   * given captured type C contains C itself) which might lead to infinite loops.
   */
  protected Printer printer =
      new Printer() {

        @Override
        protected String localize(Locale locale, String key, Object... args) {
          return AbstractDiagnosticFormatter.this.localize(locale, key, args);
        }

        @Override
        protected String capturedVarId(CapturedType t, Locale locale) {
          return "" + (allCaptured.indexOf(t) + 1);
        }

        @Override
        public String visitCapturedType(CapturedType t, Locale locale) {
          if (!allCaptured.contains(t)) {
            allCaptured = allCaptured.append(t);
          }
          return super.visitCapturedType(t, locale);
        }
      };
}
 @Override
 protected String capturedVarId(CapturedType t, Locale locale) {
   return "" + (allCaptured.indexOf(t) + 1);
 }
 @Override
 public String format(JCDiagnostic d, Locale locale) {
   allCaptured = List.nil();
   return formatDiagnostic(d, locale);
 }