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

Tue, 07 May 2013 14:27:30 -0700

author
jjg
date
Tue, 07 May 2013 14:27:30 -0700
changeset 1728
43c2f7cb9c76
parent 1678
c635a966ce84
child 1798
5cd3cb69c8b3
permissions
-rw-r--r--

8004082: test/tools/javac/plugin/showtype/Test.java fails on windows: jtreg can't delete plugin.jar
Reviewed-by: vromero, mcimadamore

     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.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     /**
   129      * Sets the type/symbol printer used by this formatter.
   130      * @param printer the rich printer to be set
   131      */
   132     protected void setRichPrinter(RichPrinter printer) {
   133         this.printer = printer;
   134         formatter.setPrinter(printer);
   135     }
   137     /**
   138      * Gets the type/symbol printer used by this formatter.
   139      * @return type/symbol rich printer
   140      */
   141     protected RichPrinter getRichPrinter() {
   142         return printer;
   143     }
   145     /**
   146      * Preprocess a given diagnostic by looking both into its arguments and into
   147      * its subdiagnostics (if any). This preprocessing is responsible for
   148      * generating info corresponding to features like where clauses, name
   149      * simplification, etc.
   150      *
   151      * @param diag the diagnostic to be preprocessed
   152      */
   153     protected void preprocessDiagnostic(JCDiagnostic diag) {
   154         for (Object o : diag.getArgs()) {
   155             if (o != null) {
   156                 preprocessArgument(o);
   157             }
   158         }
   159         if (diag.isMultiline()) {
   160             for (JCDiagnostic d : diag.getSubdiagnostics())
   161                 preprocessDiagnostic(d);
   162         }
   163     }
   165     /**
   166      * Preprocess a diagnostic argument. A type/symbol argument is
   167      * preprocessed by specialized type/symbol preprocessors.
   168      *
   169      * @param arg the argument to be translated
   170      */
   171     protected void preprocessArgument(Object arg) {
   172         if (arg instanceof Type) {
   173             preprocessType((Type)arg);
   174         }
   175         else if (arg instanceof Symbol) {
   176             preprocessSymbol((Symbol)arg);
   177         }
   178         else if (arg instanceof JCDiagnostic) {
   179             preprocessDiagnostic((JCDiagnostic)arg);
   180         }
   181         else if (arg instanceof Iterable<?>) {
   182             for (Object o : (Iterable<?>)arg) {
   183                 preprocessArgument(o);
   184             }
   185         }
   186     }
   188     /**
   189      * Build a list of multiline diagnostics containing detailed info about
   190      * type-variables, captured types, and intersection types
   191      *
   192      * @return where clause list
   193      */
   194     protected List<JCDiagnostic> getWhereClauses() {
   195         List<JCDiagnostic> clauses = List.nil();
   196         for (WhereClauseKind kind : WhereClauseKind.values()) {
   197             List<JCDiagnostic> lines = List.nil();
   198             for (Map.Entry<Type, JCDiagnostic> entry : whereClauses.get(kind).entrySet()) {
   199                 lines = lines.prepend(entry.getValue());
   200             }
   201             if (!lines.isEmpty()) {
   202                 String key = kind.key();
   203                 if (lines.size() > 1)
   204                     key += ".1";
   205                 JCDiagnostic d = diags.fragment(key, whereClauses.get(kind).keySet());
   206                 d = new JCDiagnostic.MultilineDiagnostic(d, lines.reverse());
   207                 clauses = clauses.prepend(d);
   208             }
   209         }
   210         return clauses.reverse();
   211     }
   213     private int indexOf(Type type, WhereClauseKind kind) {
   214         int index = 1;
   215         for (Type t : whereClauses.get(kind).keySet()) {
   216             if (t.tsym == type.tsym) {
   217                 return index;
   218             }
   219             if (kind != WhereClauseKind.TYPEVAR ||
   220                     t.toString().equals(type.toString())) {
   221                 index++;
   222             }
   223         }
   224         return -1;
   225     }
   227     private boolean unique(TypeVar typevar) {
   228         int found = 0;
   229         for (Type t : whereClauses.get(WhereClauseKind.TYPEVAR).keySet()) {
   230             if (t.toString().equals(typevar.toString())) {
   231                 found++;
   232             }
   233         }
   234         if (found < 1)
   235             throw new AssertionError("Missing type variable in where clause " + typevar);
   236         return found == 1;
   237     }
   238     //where
   239     /**
   240      * This enum defines all posssible kinds of where clauses that can be
   241      * attached by a rich diagnostic formatter to a given diagnostic
   242      */
   243     enum WhereClauseKind {
   245         /** where clause regarding a type variable */
   246         TYPEVAR("where.description.typevar"),
   247         /** where clause regarding a captured type */
   248         CAPTURED("where.description.captured"),
   249         /** where clause regarding an intersection type */
   250         INTERSECTION("where.description.intersection");
   252         /** resource key for this where clause kind */
   253         private final String key;
   255         WhereClauseKind(String key) {
   256             this.key = key;
   257         }
   259         String key() {
   260             return key;
   261         }
   262     }
   264     // <editor-fold defaultstate="collapsed" desc="name simplifier">
   265     /**
   266      * A name simplifier keeps track of class names usages in order to determine
   267      * whether a class name can be compacted or not. Short names are not used
   268      * if a conflict is detected, e.g. when two classes with the same simple
   269      * name belong to different packages - in this case the formatter reverts
   270      * to fullnames as compact names might lead to a confusing diagnostic.
   271      */
   272     protected class ClassNameSimplifier {
   274         /* table for keeping track of all short name usages */
   275         Map<Name, List<Symbol>> nameClashes = new HashMap<Name, List<Symbol>>();
   277         /**
   278          * Add a name usage to the simplifier's internal cache
   279          */
   280         protected void addUsage(Symbol sym) {
   281             Name n = sym.getSimpleName();
   282             List<Symbol> conflicts = nameClashes.get(n);
   283             if (conflicts == null) {
   284                 conflicts = List.nil();
   285             }
   286             if (!conflicts.contains(sym))
   287                 nameClashes.put(n, conflicts.append(sym));
   288         }
   290         public String simplify(Symbol s) {
   291             String name = s.getQualifiedName().toString();
   292             if (!s.type.isCompound() && !s.type.isPrimitive()) {
   293                 List<Symbol> conflicts = nameClashes.get(s.getSimpleName());
   294                 if (conflicts == null ||
   295                     (conflicts.size() == 1 &&
   296                     conflicts.contains(s))) {
   297                     List<Name> l = List.nil();
   298                     Symbol s2 = s;
   299                     while (s2.type.getEnclosingType().hasTag(CLASS)
   300                             && s2.owner.kind == Kinds.TYP) {
   301                         l = l.prepend(s2.getSimpleName());
   302                         s2 = s2.owner;
   303                     }
   304                     l = l.prepend(s2.getSimpleName());
   305                     StringBuilder buf = new StringBuilder();
   306                     String sep = "";
   307                     for (Name n2 : l) {
   308                         buf.append(sep);
   309                         buf.append(n2);
   310                         sep = ".";
   311                     }
   312                     name = buf.toString();
   313                 }
   314             }
   315             return name;
   316         }
   317     };
   318     // </editor-fold>
   320     // <editor-fold defaultstate="collapsed" desc="rich printer">
   321     /**
   322      * Enhanced type/symbol printer that provides support for features like simple names
   323      * and type variable disambiguation. This enriched printer exploits the info
   324      * discovered during type/symbol preprocessing. This printer is set on the delegate
   325      * formatter so that rich type/symbol info can be properly rendered.
   326      */
   327     protected class RichPrinter extends Printer {
   329         @Override
   330         public String localize(Locale locale, String key, Object... args) {
   331             return formatter.localize(locale, key, args);
   332         }
   334         @Override
   335         public String capturedVarId(CapturedType t, Locale locale) {
   336             return indexOf(t, WhereClauseKind.CAPTURED) + "";
   337         }
   339         @Override
   340         public String visitType(Type t, Locale locale) {
   341             String s = super.visitType(t, locale);
   342             if (t == syms.botType)
   343                 s = localize(locale, "compiler.misc.type.null");
   344             return s;
   345         }
   347         @Override
   348         public String visitCapturedType(CapturedType t, Locale locale) {
   349             if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
   350                 return localize(locale,
   351                     "compiler.misc.captured.type",
   352                     indexOf(t, WhereClauseKind.CAPTURED));
   353             }
   354             else
   355                 return super.visitCapturedType(t, locale);
   356         }
   358         @Override
   359         public String visitClassType(ClassType t, Locale locale) {
   360             if (t.isCompound() &&
   361                     getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) {
   362                 return localize(locale,
   363                         "compiler.misc.intersection.type",
   364                         indexOf(t, WhereClauseKind.INTERSECTION));
   365             }
   366             else
   367                 return super.visitClassType(t, locale);
   368         }
   370         @Override
   371         protected String className(ClassType t, boolean longform, Locale locale) {
   372             Symbol sym = t.tsym;
   373             if (sym.name.length() == 0 ||
   374                     !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) {
   375                 return super.className(t, longform, locale);
   376             }
   377             else if (longform)
   378                 return nameSimplifier.simplify(sym).toString();
   379             else
   380                 return sym.name.toString();
   381         }
   383         @Override
   384         public String visitTypeVar(TypeVar t, Locale locale) {
   385             if (unique(t) ||
   386                     !getConfiguration().isEnabled(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES)) {
   387                 return t.toString();
   388             }
   389             else {
   390                 return localize(locale,
   391                         "compiler.misc.type.var",
   392                         t.toString(), indexOf(t, WhereClauseKind.TYPEVAR));
   393             }
   394         }
   396         @Override
   397         public String visitClassSymbol(ClassSymbol s, Locale locale) {
   398             if (s.type.isCompound()) {
   399                 return visit(s.type, locale);
   400             }
   401             String name = nameSimplifier.simplify(s);
   402             if (name.length() == 0 ||
   403                     !getConfiguration().isEnabled(RichFormatterFeature.SIMPLE_NAMES)) {
   404                 return super.visitClassSymbol(s, locale);
   405             }
   406             else {
   407                 return name;
   408             }
   409         }
   411         @Override
   412         public String visitMethodSymbol(MethodSymbol s, Locale locale) {
   413             String ownerName = visit(s.owner, locale);
   414             if (s.isStaticOrInstanceInit()) {
   415                return ownerName;
   416             } else {
   417                 String ms = (s.name == s.name.table.names.init)
   418                     ? ownerName
   419                     : s.name.toString();
   420                 if (s.type != null) {
   421                     if (s.type.hasTag(FORALL)) {
   422                         ms = "<" + visitTypes(s.type.getTypeArguments(), locale) + ">" + ms;
   423                     }
   424                     ms += "(" + printMethodArgs(
   425                             s.type.getParameterTypes(),
   426                             (s.flags() & VARARGS) != 0,
   427                             locale) + ")";
   428                 }
   429                 return ms;
   430             }
   431         }
   432     };
   433     // </editor-fold>
   435     // <editor-fold defaultstate="collapsed" desc="type scanner">
   436     /**
   437      * Preprocess a given type looking for (i) additional info (where clauses) to be
   438      * added to the main diagnostic (ii) names to be compacted.
   439      */
   440     protected void preprocessType(Type t) {
   441         typePreprocessor.visit(t);
   442     }
   443     //where
   444     protected Types.UnaryVisitor<Void> typePreprocessor =
   445             new Types.UnaryVisitor<Void>() {
   447         public Void visit(List<Type> ts) {
   448             for (Type t : ts)
   449                 visit(t);
   450             return null;
   451         }
   453         @Override
   454         public Void visitForAll(ForAll t, Void ignored) {
   455             visit(t.tvars);
   456             visit(t.qtype);
   457             return null;
   458         }
   460         @Override
   461         public Void visitMethodType(MethodType t, Void ignored) {
   462             visit(t.argtypes);
   463             visit(t.restype);
   464             return null;
   465         }
   467         @Override
   468         public Void visitErrorType(ErrorType t, Void ignored) {
   469             Type ot = t.getOriginalType();
   470             if (ot != null)
   471                 visit(ot);
   472             return null;
   473         }
   475         @Override
   476         public Void visitArrayType(ArrayType t, Void ignored) {
   477             visit(t.elemtype);
   478             return null;
   479         }
   481         @Override
   482         public Void visitWildcardType(WildcardType t, Void ignored) {
   483             visit(t.type);
   484             return null;
   485         }
   487         public Void visitType(Type t, Void ignored) {
   488             return null;
   489         }
   491         @Override
   492         public Void visitCapturedType(CapturedType t, Void ignored) {
   493             if (indexOf(t, WhereClauseKind.CAPTURED) == -1) {
   494                 String suffix = t.lower == syms.botType ? ".1" : "";
   495                 JCDiagnostic d = diags.fragment("where.captured"+ suffix, t, t.bound, t.lower, t.wildcard);
   496                 whereClauses.get(WhereClauseKind.CAPTURED).put(t, d);
   497                 visit(t.wildcard);
   498                 visit(t.lower);
   499                 visit(t.bound);
   500             }
   501             return null;
   502         }
   504         @Override
   505         public Void visitClassType(ClassType t, Void ignored) {
   506             if (t.isCompound()) {
   507                 if (indexOf(t, WhereClauseKind.INTERSECTION) == -1) {
   508                     Type supertype = types.supertype(t);
   509                     List<Type> interfaces = types.interfaces(t);
   510                     JCDiagnostic d = diags.fragment("where.intersection", t, interfaces.prepend(supertype));
   511                     whereClauses.get(WhereClauseKind.INTERSECTION).put(t, d);
   512                     visit(supertype);
   513                     visit(interfaces);
   514                 }
   515             } else if (t.tsym.name.isEmpty()) {
   516                 //anon class
   517                 ClassType norm = (ClassType) t.tsym.type;
   518                 if (norm != null) {
   519                     if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
   520                         visit(norm.interfaces_field.head);
   521                     } else {
   522                         visit(norm.supertype_field);
   523                     }
   524                 }
   525             }
   526             nameSimplifier.addUsage(t.tsym);
   527             visit(t.getTypeArguments());
   528             if (t.getEnclosingType() != Type.noType)
   529                 visit(t.getEnclosingType());
   530             return null;
   531         }
   533         @Override
   534         public Void visitTypeVar(TypeVar t, Void ignored) {
   535             if (indexOf(t, WhereClauseKind.TYPEVAR) == -1) {
   536                 //access the bound type and skip error types
   537                 Type bound = t.bound;
   538                 while ((bound instanceof ErrorType))
   539                     bound = ((ErrorType)bound).getOriginalType();
   540                 //retrieve the bound list - if the type variable
   541                 //has not been attributed the bound is not set
   542                 List<Type> bounds = (bound != null) &&
   543                         (bound.hasTag(CLASS) || bound.hasTag(TYPEVAR)) ?
   544                     types.getBounds(t) :
   545                     List.<Type>nil();
   547                 nameSimplifier.addUsage(t.tsym);
   549                 boolean boundErroneous = bounds.head == null ||
   550                                          bounds.head.hasTag(NONE) ||
   551                                          bounds.head.hasTag(ERROR);
   553                 if ((t.tsym.flags() & SYNTHETIC) == 0) {
   554                     //this is a true typevar
   555                     JCDiagnostic d = diags.fragment("where.typevar" +
   556                         (boundErroneous ? ".1" : ""), t, bounds,
   557                         Kinds.kindName(t.tsym.location()), t.tsym.location());
   558                     whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
   559                     symbolPreprocessor.visit(t.tsym.location(), null);
   560                     visit(bounds);
   561                 } else {
   562                     Assert.check(!boundErroneous);
   563                     //this is a fresh (synthetic) tvar
   564                     JCDiagnostic d = diags.fragment("where.fresh.typevar", t, bounds);
   565                     whereClauses.get(WhereClauseKind.TYPEVAR).put(t, d);
   566                     visit(bounds);
   567                 }
   569             }
   570             return null;
   571         }
   572     };
   573     // </editor-fold>
   575     // <editor-fold defaultstate="collapsed" desc="symbol scanner">
   576     /**
   577      * Preprocess a given symbol looking for (i) additional info (where clauses) to be
   578      * added to the main diagnostic (ii) names to be compacted
   579      */
   580     protected void preprocessSymbol(Symbol s) {
   581         symbolPreprocessor.visit(s, null);
   582     }
   583     //where
   584     protected Types.DefaultSymbolVisitor<Void, Void> symbolPreprocessor =
   585             new Types.DefaultSymbolVisitor<Void, Void>() {
   587         @Override
   588         public Void visitClassSymbol(ClassSymbol s, Void ignored) {
   589             if (s.type.isCompound()) {
   590                 typePreprocessor.visit(s.type);
   591             } else {
   592                 nameSimplifier.addUsage(s);
   593             }
   594             return null;
   595         }
   597         @Override
   598         public Void visitSymbol(Symbol s, Void ignored) {
   599             return null;
   600         }
   602         @Override
   603         public Void visitMethodSymbol(MethodSymbol s, Void ignored) {
   604             visit(s.owner, null);
   605             if (s.type != null)
   606                 typePreprocessor.visit(s.type);
   607             return null;
   608         }
   609     };
   610     // </editor-fold>
   612     @Override
   613     public RichConfiguration getConfiguration() {
   614         //the following cast is always safe - see init
   615         return (RichConfiguration)configuration;
   616     }
   618     /**
   619      * Configuration object provided by the rich formatter.
   620      */
   621     public static class RichConfiguration extends ForwardingDiagnosticFormatter.ForwardingConfiguration {
   623         /** set of enabled rich formatter's features */
   624         protected java.util.EnumSet<RichFormatterFeature> features;
   626         @SuppressWarnings("fallthrough")
   627         public RichConfiguration(Options options, AbstractDiagnosticFormatter formatter) {
   628             super(formatter.getConfiguration());
   629             features = formatter.isRaw() ? EnumSet.noneOf(RichFormatterFeature.class) :
   630                 EnumSet.of(RichFormatterFeature.SIMPLE_NAMES,
   631                     RichFormatterFeature.WHERE_CLAUSES,
   632                     RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
   633             String diagOpts = options.get("diags");
   634             if (diagOpts != null) {
   635                 for (String args: diagOpts.split(",")) {
   636                     if (args.equals("-where")) {
   637                         features.remove(RichFormatterFeature.WHERE_CLAUSES);
   638                     }
   639                     else if (args.equals("where")) {
   640                         features.add(RichFormatterFeature.WHERE_CLAUSES);
   641                     }
   642                     if (args.equals("-simpleNames")) {
   643                         features.remove(RichFormatterFeature.SIMPLE_NAMES);
   644                     }
   645                     else if (args.equals("simpleNames")) {
   646                         features.add(RichFormatterFeature.SIMPLE_NAMES);
   647                     }
   648                     if (args.equals("-disambiguateTvars")) {
   649                         features.remove(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
   650                     }
   651                     else if (args.equals("disambiguateTvars")) {
   652                         features.add(RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
   653                     }
   654                 }
   655             }
   656         }
   658         /**
   659          * Returns a list of all the features supported by the rich formatter.
   660          * @return list of supported features
   661          */
   662         public RichFormatterFeature[] getAvailableFeatures() {
   663             return RichFormatterFeature.values();
   664         }
   666         /**
   667          * Enable a specific feature on this rich formatter.
   668          * @param feature feature to be enabled
   669          */
   670         public void enable(RichFormatterFeature feature) {
   671             features.add(feature);
   672         }
   674         /**
   675          * Disable a specific feature on this rich formatter.
   676          * @param feature feature to be disabled
   677          */
   678         public void disable(RichFormatterFeature feature) {
   679             features.remove(feature);
   680         }
   682         /**
   683          * Is a given feature enabled on this formatter?
   684          * @param feature feature to be tested
   685          */
   686         public boolean isEnabled(RichFormatterFeature feature) {
   687             return features.contains(feature);
   688         }
   690         /**
   691          * The advanced formatting features provided by the rich formatter
   692          */
   693         public enum RichFormatterFeature {
   694             /** a list of additional info regarding a given type/symbol */
   695             WHERE_CLAUSES,
   696             /** full class names simplification (where possible) */
   697             SIMPLE_NAMES,
   698             /** type-variable names disambiguation */
   699             UNIQUE_TYPEVAR_NAMES;
   700         }
   701     }
   702 }

mercurial