src/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java

Tue, 24 Dec 2013 09:17:37 -0800

author
ksrini
date
Tue, 24 Dec 2013 09:17:37 -0800
changeset 2227
998b10c43157
parent 1887
7b756b307e12
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8029230: Update copyright year to match last edit in jdk8 langtools repository for 2013
Reviewed-by: ksrini
Contributed-by: steve.sides@oracle.com

     1 /*
     2  * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 package com.sun.tools.javac.util;
    27 import java.util.EnumMap;
    28 import java.util.EnumSet;
    29 import java.util.HashMap;
    30 import java.util.LinkedHashMap;
    31 import java.util.Locale;
    32 import java.util.Map;
    34 import com.sun.tools.javac.code.Kinds;
    35 import com.sun.tools.javac.code.Printer;
    36 import com.sun.tools.javac.code.Symbol;
    37 import com.sun.tools.javac.code.Symbol.*;
    38 import com.sun.tools.javac.code.Symtab;
    39 import com.sun.tools.javac.code.Type;
    40 import com.sun.tools.javac.code.Type.*;
    41 import com.sun.tools.javac.code.Types;
    43 import static com.sun.tools.javac.code.TypeTag.*;
    44 import static com.sun.tools.javac.code.Flags.*;
    45 import static com.sun.tools.javac.util.LayoutCharacters.*;
    46 import static com.sun.tools.javac.util.RichDiagnosticFormatter.RichConfiguration.*;
    48 /**
    49  * A rich diagnostic formatter is a formatter that provides better integration
    50  * with javac's type system. A diagostic is first preprocessed in order to keep
    51  * track of each types/symbols in it; after these informations are collected,
    52  * the diagnostic is rendered using a standard formatter, whose type/symbol printer
    53  * has been replaced by a more refined version provided by this rich formatter.
    54  * The rich formatter currently enables three different features: (i) simple class
    55  * names - that is class names are displayed used a non qualified name (thus
    56  * omitting package info) whenever possible - (ii) where clause list - a list of
    57  * additional subdiagnostics that provide specific info about type-variables,
    58  * captured types, intersection types that occur in the diagnostic that is to be
    59  * formatted and (iii) type-variable disambiguation - when the diagnostic refers
    60  * to two different type-variables with the same name, their representation is
    61  * disambiguated by appending an index to the type variable name.
    62  *
    63  * <p><b>This is NOT part of any supported API.
    64  * If you write code that depends on this, you do so at your own risk.
    65  * This code and its internal interfaces are subject to change or
    66  * deletion without notice.</b>
    67  */
    68 public class RichDiagnosticFormatter extends
    69         ForwardingDiagnosticFormatter<JCDiagnostic, AbstractDiagnosticFormatter> {
    71     final Symtab syms;
    72     final Types types;
    73     final JCDiagnostic.Factory diags;
    74     final JavacMessages messages;
    76     /* name simplifier used by this formatter */
    77     protected ClassNameSimplifier nameSimplifier;
    79     /* type/symbol printer used by this formatter */
    80     private RichPrinter printer;
    82     /* map for keeping track of a where clause associated to a given type */
    83     Map<WhereClauseKind, Map<Type, JCDiagnostic>> whereClauses;
    85     /** Get the DiagnosticFormatter instance for this context. */
    86     public static RichDiagnosticFormatter instance(Context context) {
    87         RichDiagnosticFormatter instance = context.get(RichDiagnosticFormatter.class);
    88         if (instance == null)
    89             instance = new RichDiagnosticFormatter(context);
    90         return instance;
    91     }
    93     protected RichDiagnosticFormatter(Context context) {
    94         super((AbstractDiagnosticFormatter)Log.instance(context).getDiagnosticFormatter());
    95         setRichPrinter(new RichPrinter());
    96         this.syms = Symtab.instance(context);
    97         this.diags = JCDiagnostic.Factory.instance(context);
    98         this.types = Types.instance(context);
    99         this.messages = JavacMessages.instance(context);
   100         whereClauses = new EnumMap<WhereClauseKind, Map<Type, JCDiagnostic>>(WhereClauseKind.class);
   101         configuration = new RichConfiguration(Options.instance(context), formatter);
   102         for (WhereClauseKind kind : WhereClauseKind.values())
   103             whereClauses.put(kind, new LinkedHashMap<Type, JCDiagnostic>());
   104     }
   106     @Override
   107     public String format(JCDiagnostic diag, Locale l) {
   108         StringBuilder sb = new StringBuilder();
   109         nameSimplifier = new ClassNameSimplifier();
   110         for (WhereClauseKind kind : WhereClauseKind.values())
   111             whereClauses.get(kind).clear();
   112         preprocessDiagnostic(diag);
   113         sb.append(formatter.format(diag, l));
   114         if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
   115             List<JCDiagnostic> clauses = getWhereClauses();
   116             String indent = formatter.isRaw() ? "" :
   117                 formatter.indentString(DetailsInc);
   118             for (JCDiagnostic d : clauses) {
   119                 String whereClause = formatter.format(d, l);
   120                 if (whereClause.length() > 0) {
   121                     sb.append('\n' + indent + whereClause);
   122                 }
   123             }
   124         }
   125         return sb.toString();
   126     }
   128     @Override
   129     public String formatMessage(JCDiagnostic diag, Locale l) {
   130         nameSimplifier = new ClassNameSimplifier();
   131         preprocessDiagnostic(diag);
   132         return super.formatMessage(diag, l);
   133     }
   135     /**
   136      * Sets the type/symbol printer used by this formatter.
   137      * @param printer the rich printer to be set
   138      */
   139     protected void setRichPrinter(RichPrinter printer) {
   140         this.printer = printer;
   141         formatter.setPrinter(printer);
   142     }
   144     /**
   145      * Gets the type/symbol printer used by this formatter.
   146      * @return type/symbol rich printer
   147      */
   148     protected RichPrinter getRichPrinter() {
   149         return printer;
   150     }
   152     /**
   153      * Preprocess a given diagnostic by looking both into its arguments and into
   154      * its subdiagnostics (if any). This preprocessing is responsible for
   155      * generating info corresponding to features like where clauses, name
   156      * simplification, etc.
   157      *
   158      * @param diag the diagnostic to be preprocessed
   159      */
   160     protected void preprocessDiagnostic(JCDiagnostic diag) {
   161         for (Object o : diag.getArgs()) {
   162             if (o != null) {
   163                 preprocessArgument(o);
   164             }
   165         }
   166         if (diag.isMultiline()) {
   167             for (JCDiagnostic d : diag.getSubdiagnostics())
   168                 preprocessDiagnostic(d);
   169         }
   170     }
   172     /**
   173      * Preprocess a diagnostic argument. A type/symbol argument is
   174      * preprocessed by specialized type/symbol preprocessors.
   175      *
   176      * @param arg the argument to be translated
   177      */
   178     protected void preprocessArgument(Object arg) {
   179         if (arg instanceof Type) {
   180             preprocessType((Type)arg);
   181         }
   182         else if (arg instanceof Symbol) {
   183             preprocessSymbol((Symbol)arg);
   184         }
   185         else if (arg instanceof JCDiagnostic) {
   186             preprocessDiagnostic((JCDiagnostic)arg);
   187         }
   188         else if (arg instanceof Iterable<?>) {
   189             for (Object o : (Iterable<?>)arg) {
   190                 preprocessArgument(o);
   191             }
   192         }
   193     }
   195     /**
   196      * Build a list of multiline diagnostics containing detailed info about
   197      * type-variables, captured types, and intersection types
   198      *
   199      * @return where clause list
   200      */
   201     protected List<JCDiagnostic> getWhereClauses() {
   202         List<JCDiagnostic> clauses = List.nil();
   203         for (WhereClauseKind kind : WhereClauseKind.values()) {
   204             List<JCDiagnostic> lines = List.nil();
   205             for (Map.Entry<Type, JCDiagnostic> entry : whereClauses.get(kind).entrySet()) {
   206                 lines = lines.prepend(entry.getValue());
   207             }
   208             if (!lines.isEmpty()) {
   209                 String key = kind.key();
   210                 if (lines.size() > 1)
   211                     key += ".1";
   212                 JCDiagnostic d = diags.fragment(key, whereClauses.get(kind).keySet());
   213                 d = new JCDiagnostic.MultilineDiagnostic(d, lines.reverse());
   214                 clauses = clauses.prepend(d);
   215             }
   216         }
   217         return clauses.reverse();
   218     }
   220     private int indexOf(Type type, WhereClauseKind kind) {
   221         int index = 1;
   222         for (Type t : whereClauses.get(kind).keySet()) {
   223             if (t.tsym == type.tsym) {
   224                 return index;
   225             }
   226             if (kind != WhereClauseKind.TYPEVAR ||
   227                     t.toString().equals(type.toString())) {
   228                 index++;
   229             }
   230         }
   231         return -1;
   232     }
   234     private boolean unique(TypeVar typevar) {
   235         int found = 0;
   236         for (Type t : whereClauses.get(WhereClauseKind.TYPEVAR).keySet()) {
   237             if (t.toString().equals(typevar.toString())) {
   238                 found++;
   239             }
   240         }
   241         if (found < 1)
   242             throw new AssertionError("Missing type variable in where clause " + typevar);
   243         return found == 1;
   244     }
   245     //where
   246     /**
   247      * This enum defines all posssible kinds of where clauses that can be
   248      * attached by a rich diagnostic formatter to a given diagnostic
   249      */
   250     enum WhereClauseKind {
   252         /** where clause regarding a type variable */
   253         TYPEVAR("where.description.typevar"),
   254         /** where clause regarding a captured type */
   255         CAPTURED("where.description.captured"),
   256         /** where clause regarding an intersection type */
   257         INTERSECTION("where.description.intersection");
   259         /** resource key for this where clause kind */
   260         private final String key;
   262         WhereClauseKind(String key) {
   263             this.key = key;
   264         }
   266         String key() {
   267             return key;
   268         }
   269     }
   271     // <editor-fold defaultstate="collapsed" desc="name simplifier">
   272     /**
   273      * A name simplifier keeps track of class names usages in order to determine
   274      * whether a class name can be compacted or not. Short names are not used
   275      * if a conflict is detected, e.g. when two classes with the same simple
   276      * name belong to different packages - in this case the formatter reverts
   277      * to fullnames as compact names might lead to a confusing diagnostic.
   278      */
   279     protected class ClassNameSimplifier {
   281         /* table for keeping track of all short name usages */
   282         Map<Name, List<Symbol>> nameClashes = new HashMap<Name, List<Symbol>>();
   284         /**
   285          * Add a name usage to the simplifier's internal cache
   286          */
   287         protected void addUsage(Symbol sym) {
   288             Name n = sym.getSimpleName();
   289             List<Symbol> conflicts = nameClashes.get(n);
   290             if (conflicts == null) {
   291                 conflicts = List.nil();
   292             }
   293             if (!conflicts.contains(sym))
   294                 nameClashes.put(n, conflicts.append(sym));
   295         }
   297         public String simplify(Symbol s) {
   298             String name = s.getQualifiedName().toString();
   299             if (!s.type.isCompound() && !s.type.isPrimitive()) {
   300                 List<Symbol> conflicts = nameClashes.get(s.getSimpleName());
   301                 if (conflicts == null ||
   302                     (conflicts.size() == 1 &&
   303                     conflicts.contains(s))) {
   304                     List<Name> l = List.nil();
   305                     Symbol s2 = s;
   306                     while (s2.type.hasTag(CLASS) &&
   307                             s2.type.getEnclosingType().hasTag(CLASS) &&
   308                             s2.owner.kind == Kinds.TYP) {
   309                         l = l.prepend(s2.getSimpleName());
   310                         s2 = s2.owner;
   311                     }
   312                     l = l.prepend(s2.getSimpleName());
   313                     StringBuilder buf = new StringBuilder();
   314                     String sep = "";
   315                     for (Name n2 : l) {
   316                         buf.append(sep);
   317                         buf.append(n2);
   318                         sep = ".";
   319                     }
   320                     name = buf.toString();
   321                 }
   322             }
   323             return name;
   324         }
   325     };
   326     // </editor-fold>
   328     // <editor-fold defaultstate="collapsed" desc="rich printer">
   329     /**
   330      * Enhanced type/symbol printer that provides support for features like simple names
   331      * and type variable disambiguation. This enriched printer exploits the info
   332      * discovered during type/symbol preprocessing. This printer is set on the delegate
   333      * formatter so that rich type/symbol info can be properly rendered.
   334      */
   335     protected class RichPrinter extends Printer {
   337         @Override
   338         public String localize(Locale locale, String key, Object... args) {
   339             return formatter.localize(locale, key, args);
   340         }
   342         @Override
   343         public String capturedVarId(CapturedType t, Locale locale) {
   344             return indexOf(t, WhereClauseKind.CAPTURED) + "";
   345         }
   347         @Override
   348         public String visitType(Type t, Locale locale) {
   349             String s = super.visitType(t, locale);
   350             if (t == syms.botType)
   351                 s = localize(locale, "compiler.misc.type.null");
   352             return s;
   353         }
   355         @Override
   356         public String visitCapturedType(CapturedType t, Locale locale) {
   357             if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
   358                 return localize(locale,
   359                     "compiler.misc.captured.type",
   360                     indexOf(t, WhereClauseKind.CAPTURED));
   361             }
   362             else
   363                 return super.visitCapturedType(t, locale);
   364         }
   366         @Override
   367         public String visitClassType(ClassType t, Locale locale) {
   368             if (t.isCompound() &&
   369                     getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
   370                 return localize(locale,
   371                         "compiler.misc.intersection.type",
   372                         indexOf(t, WhereClauseKind.INTERSECTION));
   373             }
   374             else
   375                 return super.visitClassType(t, locale);
   376         }
   378         @Override
   379         protected String className(ClassType t, boolean longform, Locale locale) {
   380             Symbol sym = t.tsym;
   381             if (sym.name.length() == 0 ||
   382                     !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) {
   383                 return super.className(t, longform, locale);
   384             }
   385             else if (longform)
   386                 return nameSimplifier.simplify(sym).toString();
   387             else
   388                 return sym.name.toString();
   389         }
   391         @Override
   392         public String visitTypeVar(TypeVar t, Locale locale) {
   393             if (unique(t) ||
   394                     !getConfiguration().isEnabled(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES)) {
   395                 return t.toString();
   396             }
   397             else {
   398                 return localize(locale,
   399                         "compiler.misc.type.var",
   400                         t.toString(), indexOf(t, WhereClauseKind.TYPEVAR));
   401             }
   402         }
   404         @Override
   405         public String visitClassSymbol(ClassSymbol s, Locale locale) {
   406             if (s.type.isCompound()) {
   407                 return visit(s.type, locale);
   408             }
   409             String name = nameSimplifier.simplify(s);
   410             if (name.length() == 0 ||
   411                     !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) {
   412                 return super.visitClassSymbol(s, locale);
   413             }
   414             else {
   415                 return name;
   416             }
   417         }
   419         @Override
   420         public String visitMethodSymbol(MethodSymbol s, Locale locale) {
   421             String ownerName = visit(s.owner, locale);
   422             if (s.isStaticOrInstanceInit()) {
   423                return ownerName;
   424             } else {
   425                 String ms = (s.name == s.name.table.names.init)
   426                     ? ownerName
   427                     : s.name.toString();
   428                 if (s.type != null) {
   429                     if (s.type.hasTag(FORALL)) {
   430                         ms = "<" + visitTypes(s.type.getTypeArguments(), locale) + ">" + ms;
   431                     }
   432                     ms += "(" + printMethodArgs(
   433                             s.type.getParameterTypes(),
   434                             (s.flags() & VARARGS) != 0,
   435                             locale) + ")";
   436                 }
   437                 return ms;
   438             }
   439         }
   440     };
   441     // </editor-fold>
   443     // <editor-fold defaultstate="collapsed" desc="type scanner">
   444     /**
   445      * Preprocess a given type looking for (i) additional info (where clauses) to be
   446      * added to the main diagnostic (ii) names to be compacted.
   447      */
   448     protected void preprocessType(Type t) {
   449         typePreprocessor.visit(t);
   450     }
   451     //where
   452     protected Types.UnaryVisitor<Void> typePreprocessor =
   453             new Types.UnaryVisitor<Void>() {
   455         public Void visit(List<Type> ts) {
   456             for (Type t : ts)
   457                 visit(t);
   458             return null;
   459         }
   461         @Override
   462         public Void visitForAll(ForAll t, Void ignored) {
   463             visit(t.tvars);
   464             visit(t.qtype);
   465             return null;
   466         }
   468         @Override
   469         public Void visitMethodType(MethodType t, Void ignored) {
   470             visit(t.argtypes);
   471             visit(t.restype);
   472             return null;
   473         }
   475         @Override
   476         public Void visitErrorType(ErrorType t, Void ignored) {
   477             Type ot = t.getOriginalType();
   478             if (ot != null)
   479                 visit(ot);
   480             return null;
   481         }
   483         @Override
   484         public Void visitArrayType(ArrayType t, Void ignored) {
   485             visit(t.elemtype);
   486             return null;
   487         }
   489         @Override
   490         public Void visitWildcardType(WildcardType t, Void ignored) {
   491             visit(t.type);
   492             return null;
   493         }
   495         public Void visitType(Type t, Void ignored) {
   496             return null;
   497         }
   499         @Override
   500         public Void visitCapturedType(CapturedType t, Void ignored) {
   501             if (indexOf(t, WhereClauseKind.CAPTURED) == -1) {
   502                 String suffix = t.lower == syms.botType ? ".1" : "";
   503                 JCDiagnostic d = diags.fragment("where.captured"+ suffix, t, t.bound, t.lower, t.wildcard);
   504                 whereClauses.get(WhereClauseKind.CAPTURED).put(t, d);
   505                 visit(t.wildcard);
   506                 visit(t.lower);
   507                 visit(t.bound);
   508             }
   509             return null;
   510         }
   512         @Override
   513         public Void visitClassType(ClassType t, Void ignored) {
   514             if (t.isCompound()) {
   515                 if (indexOf(t, WhereClauseKind.INTERSECTION) == -1) {
   516                     Type supertype = types.supertype(t);
   517                     List<Type> interfaces = types.interfaces(t);
   518                     JCDiagnostic d = diags.fragment("where.intersection", t, interfaces.prepend(supertype));
   519                     whereClauses.get(WhereClauseKind.INTERSECTION).put(t, d);
   520                     visit(supertype);
   521                     visit(interfaces);
   522                 }
   523             } else if (t.tsym.name.isEmpty()) {
   524                 //anon class
   525                 ClassType norm = (ClassType) t.tsym.type;
   526                 if (norm != null) {
   527                     if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
   528                         visit(norm.interfaces_field.head);
   529                     } else {
   530                         visit(norm.supertype_field);
   531                     }
   532                 }
   533             }
   534             nameSimplifier.addUsage(t.tsym);
   535             visit(t.getTypeArguments());
   536             if (t.getEnclosingType() != Type.noType)
   537                 visit(t.getEnclosingType());
   538             return null;
   539         }
   541         @Override
   542         public Void visitTypeVar(TypeVar t, Void ignored) {
   543             if (indexOf(t, WhereClauseKind.TYPEVAR) == -1) {
   544                 //access the bound type and skip error types
   545                 Type bound = t.bound;
   546                 while ((bound instanceof ErrorType))
   547                     bound = ((ErrorType)bound).getOriginalType();
   548                 //retrieve the bound list - if the type variable
   549                 //has not been attributed the bound is not set
   550                 List<Type> bounds = (bound != null) &&
   551                         (bound.hasTag(CLASS) || bound.hasTag(TYPEVAR)) ?
   552                     types.getBounds(t) :
   553                     List.<Type>nil();
   555                 nameSimplifier.addUsage(t.tsym);
   557                 boolean boundErroneous = bounds.head == null ||
   558                                          bounds.head.hasTag(NONE) ||
   559                                          bounds.head.hasTag(ERROR);
   561                 if ((t.tsym.flags() & SYNTHETIC) == 0) {
   562                     //this is a true typevar
   563                     JCDiagnostic d = diags.fragment("where.typevar" +
   564                         (boundErroneous ? ".1" : ""), t, bounds,
   565                         Kinds.kindName(t.tsym.location()), t.tsym.location());
   566                     whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
   567                     symbolPreprocessor.visit(t.tsym.location(), null);
   568                     visit(bounds);
   569                 } else {
   570                     Assert.check(!boundErroneous);
   571                     //this is a fresh (synthetic) tvar
   572                     JCDiagnostic d = diags.fragment("where.fresh.typevar", t, bounds);
   573                     whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
   574                     visit(bounds);
   575                 }
   577             }
   578             return null;
   579         }
   580     };
   581     // </editor-fold>
   583     // <editor-fold defaultstate="collapsed" desc="symbol scanner">
   584     /**
   585      * Preprocess a given symbol looking for (i) additional info (where clauses) to be
   586      * added to the main diagnostic (ii) names to be compacted
   587      */
   588     protected void preprocessSymbol(Symbol s) {
   589         symbolPreprocessor.visit(s, null);
   590     }
   591     //where
   592     protected Types.DefaultSymbolVisitor<Void, Void> symbolPreprocessor =
   593             new Types.DefaultSymbolVisitor<Void, Void>() {
   595         @Override
   596         public Void visitClassSymbol(ClassSymbol s, Void ignored) {
   597             if (s.type.isCompound()) {
   598                 typePreprocessor.visit(s.type);
   599             } else {
   600                 nameSimplifier.addUsage(s);
   601             }
   602             return null;
   603         }
   605         @Override
   606         public Void visitSymbol(Symbol s, Void ignored) {
   607             return null;
   608         }
   610         @Override
   611         public Void visitMethodSymbol(MethodSymbol s, Void ignored) {
   612             visit(s.owner, null);
   613             if (s.type != null)
   614                 typePreprocessor.visit(s.type);
   615             return null;
   616         }
   617     };
   618     // </editor-fold>
   620     @Override
   621     public RichConfiguration getConfiguration() {
   622         //the following cast is always safe - see init
   623         return (RichConfiguration)configuration;
   624     }
   626     /**
   627      * Configuration object provided by the rich formatter.
   628      */
   629     public static class RichConfiguration extends ForwardingDiagnosticFormatter.ForwardingConfiguration {
   631         /** set of enabled rich formatter's features */
   632         protected java.util.EnumSet<RichFormatterFeature> features;
   634         @SuppressWarnings("fallthrough")
   635         public RichConfiguration(Options options, AbstractDiagnosticFormatter formatter) {
   636             super(formatter.getConfiguration());
   637             features = formatter.isRaw() ? EnumSet.noneOf(RichFormatterFeature.class) :
   638                 EnumSet.of(RichFormatterFeature.SIMPLE_NAMES,
   639                     RichFormatterFeature.WHERE_CLAUSES,
   640                     RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
   641             String diagOpts = options.get("diags");
   642             if (diagOpts != null) {
   643                 for (String args: diagOpts.split(",")) {
   644                     if (args.equals("-where")) {
   645                         features.remove(RichFormatterFeature.WHERE_CLAUSES);
   646                     }
   647                     else if (args.equals("where")) {
   648                         features.add(RichFormatterFeature.WHERE_CLAUSES);
   649                     }
   650                     if (args.equals("-simpleNames")) {
   651                         features.remove(RichFormatterFeature.SIMPLE_NAMES);
   652                     }
   653                     else if (args.equals("simpleNames")) {
   654                         features.add(RichFormatterFeature.SIMPLE_NAMES);
   655                     }
   656                     if (args.equals("-disambiguateTvars")) {
   657                         features.remove(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
   658                     }
   659                     else if (args.equals("disambiguateTvars")) {
   660                         features.add(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
   661                     }
   662                 }
   663             }
   664         }
   666         /**
   667          * Returns a list of all the features supported by the rich formatter.
   668          * @return list of supported features
   669          */
   670         public RichFormatterFeature[] getAvailableFeatures() {
   671             return RichFormatterFeature.values();
   672         }
   674         /**
   675          * Enable a specific feature on this rich formatter.
   676          * @param feature feature to be enabled
   677          */
   678         public void enable(RichFormatterFeature feature) {
   679             features.add(feature);
   680         }
   682         /**
   683          * Disable a specific feature on this rich formatter.
   684          * @param feature feature to be disabled
   685          */
   686         public void disable(RichFormatterFeature feature) {
   687             features.remove(feature);
   688         }
   690         /**
   691          * Is a given feature enabled on this formatter?
   692          * @param feature feature to be tested
   693          */
   694         public boolean isEnabled(RichFormatterFeature feature) {
   695             return features.contains(feature);
   696         }
   698         /**
   699          * The advanced formatting features provided by the rich formatter
   700          */
   701         public enum RichFormatterFeature {
   702             /** a list of additional info regarding a given type/symbol */
   703             WHERE_CLAUSES,
   704             /** full class names simplification (where possible) */
   705             SIMPLE_NAMES,
   706             /** type-variable names disambiguation */
   707             UNIQUE_TYPEVAR_NAMES;
   708         }
   709     }
   710 }

mercurial