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

Mon, 10 Dec 2012 16:21:26 +0000

author
vromero
date
Mon, 10 Dec 2012 16:21:26 +0000
changeset 1442
fcf89720ae71
parent 1415
01c9d4161882
child 1497
7aa2025bbb7b
child 1569
475eb15dfdad
permissions
-rw-r--r--

8003967: detect and remove all mutable implicit static enum fields in langtools
Reviewed-by: jjg

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

mercurial