6735840: Need a type/symbol visitor printer
authormcimadamore
Mon, 29 Sep 2008 11:48:09 +0100
changeset 1357 e2d4f3e1f805
parent 1356 9f40f16e543b
child 1358 a51c5f89f8af
6735840: Need a type/symbol visitor printer Summary: Added type/symbol visitor printer with support for localization Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/share/classes/com/sun/tools/javac/code/Types.java
langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Mon Sep 29 11:34:43 2008 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Mon Sep 29 11:48:09 2008 +0100
@@ -132,6 +132,10 @@
         throw new AssertionError();
     }
 
+    public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+        return v.visitSymbol(this, p);
+    }
+
     /** The Java source which this symbol represents.
      *  A description of this symbol; overrides Object.
      */
@@ -477,6 +481,10 @@
         public <R, P> R accept(ElementVisitor<R, P> v, P p) {
             return other.accept(v, p);
         }
+
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitSymbol(other, p);
+        }
     }
 
     /** A class for type symbols. Type variables are represented by instances
@@ -570,6 +578,10 @@
             return v.visitTypeParameter(this, p);
         }
 
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitTypeSymbol(this, p);
+        }
+
         public List<Type> getBounds() {
             TypeVar t = (TypeVar)type;
             Type bound = t.getUpperBound();
@@ -653,6 +665,10 @@
         public <R, P> R accept(ElementVisitor<R, P> v, P p) {
             return v.visitPackage(this, p);
         }
+
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitPackageSymbol(this, p);
+        }
     }
 
     /** A class for class symbols
@@ -843,6 +859,10 @@
         public <R, P> R accept(ElementVisitor<R, P> v, P p) {
             return v.visitType(this, p);
         }
+
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitClassSymbol(this, p);
+        }
     }
 
 
@@ -969,6 +989,10 @@
             assert !(data instanceof Env<?>) : this;
             this.data = data;
         }
+
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitVarSymbol(this, p);
+        }
     }
 
     /** A class for method symbols.
@@ -1232,6 +1256,10 @@
             return v.visitExecutable(this, p);
         }
 
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitMethodSymbol(this, p);
+        }
+
         public Type getReturnType() {
             return asType().getReturnType();
         }
@@ -1251,6 +1279,10 @@
             super(PUBLIC | STATIC, name, type, owner);
             this.opcode = opcode;
         }
+
+        public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
+            return v.visitOperatorSymbol(this, p);
+        }
     }
 
     /** Symbol completer interface.
@@ -1308,4 +1340,28 @@
         }
 
     }
+
+    /**
+     * A visitor for symbols.  A visitor is used to implement operations
+     * (or relations) on symbols.  Most common operations on types are
+     * binary relations and this interface is designed for binary
+     * relations, that is, operations on the form
+     * Symbol&nbsp;&times;&nbsp;P&nbsp;&rarr;&nbsp;R.
+     * <!-- In plain text: Type x P -> R -->
+     *
+     * @param <R> the return type of the operation implemented by this
+     * visitor; use Void if no return type is needed.
+     * @param <P> the type of the second argument (the first being the
+     * symbol itself) of the operation implemented by this visitor; use
+     * Void if a second argument is not needed.
+     */
+    public interface Visitor<R,P> {
+        R visitClassSymbol(ClassSymbol s, P arg);
+        R visitMethodSymbol(MethodSymbol s, P arg);
+        R visitPackageSymbol(PackageSymbol s, P arg);
+        R visitOperatorSymbol(OperatorSymbol s, P arg);
+        R visitVarSymbol(VarSymbol s, P arg);
+        R visitTypeSymbol(TypeSymbol s, P arg);
+        R visitSymbol(Symbol s, P arg);
+    }
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java	Mon Sep 29 11:34:43 2008 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java	Mon Sep 29 11:48:09 2008 +0100
@@ -67,6 +67,7 @@
         new Context.Key<Types>();
 
     final Symtab syms;
+    final Messages messages;
     final Names names;
     final boolean allowBoxing;
     final ClassReader reader;
@@ -92,6 +93,7 @@
         source = Source.instance(context);
         chk = Check.instance(context);
         capturedName = names.fromString("<captured wildcard>");
+        messages = Messages.instance(context);
     }
     // </editor-fold>
 
@@ -1589,10 +1591,10 @@
                             syms.noSymbol);
         if (bounds.head.tag == TYPEVAR)
             // error condition, recover
-            bc.erasure_field = syms.objectType;
-        else
-            bc.erasure_field = erasure(bounds.head);
-        bc.members_field = new Scope(bc);
+                bc.erasure_field = syms.objectType;
+            else
+                bc.erasure_field = erasure(bounds.head);
+            bc.members_field = new Scope(bc);
         ClassType bt = (ClassType)bc.type;
         bt.allparams_field = List.nil();
         if (supertype != null) {
@@ -2249,10 +2251,234 @@
     }
     // </editor-fold>
 
+    // <editor-fold defaultstate="collapsed" desc="printType">
+    /**
+     * Visitor for generating a string representation of a given type
+     * accordingly to a given locale
+     */
+    public String toString(Type t, Locale locale) {
+        return typePrinter.visit(t, locale);
+    }
+    // where
+    private TypePrinter typePrinter = new TypePrinter();
+
+    public class TypePrinter extends DefaultTypeVisitor<String, Locale> {
+
+        public String visit(List<Type> ts, Locale locale) {
+            ListBuffer<String> sbuf = lb();
+            for (Type t : ts) {
+                sbuf.append(visit(t, locale));
+            }
+            return sbuf.toList().toString();
+        }
+
+        @Override
+        public String visitCapturedType(CapturedType t, Locale locale) {
+            return messages.getLocalizedString("compiler.misc.type.captureof",
+                        (t.hashCode() & 0xFFFFFFFFL) % Type.CapturedType.PRIME,
+                        visit(t.wildcard, locale));
+        }
+
+        @Override
+        public String visitForAll(ForAll t, Locale locale) {
+            return "<" + visit(t.tvars, locale) + ">" + visit(t.qtype, locale);
+        }
+
+        @Override
+        public String visitUndetVar(UndetVar t, Locale locale) {
+            if (t.inst != null) {
+                return visit(t.inst, locale);
+            } else {
+                return visit(t.qtype, locale) + "?";
+            }
+        }
+
+        @Override
+        public String visitArrayType(ArrayType t, Locale locale) {
+            return visit(t.elemtype, locale) + "[]";
+        }
+
+        @Override
+        public String visitClassType(ClassType t, Locale locale) {
+            StringBuffer buf = new StringBuffer();
+            if (t.getEnclosingType().tag == CLASS && t.tsym.owner.kind == Kinds.TYP) {
+                buf.append(visit(t.getEnclosingType(), locale));
+                buf.append(".");
+                buf.append(className(t, false, locale));
+            } else {
+                buf.append(className(t, true, locale));
+            }
+            if (t.getTypeArguments().nonEmpty()) {
+                buf.append('<');
+                buf.append(visit(t.getTypeArguments(), locale));
+                buf.append(">");
+            }
+            return buf.toString();
+        }
+
+        @Override
+        public String visitMethodType(MethodType t, Locale locale) {
+            return "(" + printMethodArgs(t.argtypes, false, locale) + ")" + visit(t.restype, locale);
+        }
+
+        @Override
+        public String visitPackageType(PackageType t, Locale locale) {
+            return t.tsym.getQualifiedName().toString();
+        }
+
+        @Override
+        public String visitWildcardType(WildcardType t, Locale locale) {
+            StringBuffer s = new StringBuffer();
+            s.append(t.kind);
+            if (t.kind != UNBOUND) {
+                s.append(visit(t.type, locale));
+            }
+            return s.toString();
+        }
+
+
+        public String visitType(Type t, Locale locale) {
+            String s = (t.tsym == null || t.tsym.name == null)
+                    ? messages.getLocalizedString("compiler.misc.type.none")
+                    : t.tsym.name.toString();
+            return s;
+        }
+
+        protected String className(ClassType t, boolean longform, Locale locale) {
+            Symbol sym = t.tsym;
+            if (sym.name.length() == 0 && (sym.flags() & COMPOUND) != 0) {
+                StringBuffer s = new StringBuffer(visit(supertype(t), locale));
+                for (List<Type> is = interfaces(t); is.nonEmpty(); is = is.tail) {
+                    s.append("&");
+                    s.append(visit(is.head, locale));
+                }
+                return s.toString();
+            } else if (sym.name.length() == 0) {
+                String s;
+                ClassType norm = (ClassType) t.tsym.type;
+                if (norm == null) {
+                    s = getLocalizedString(locale, "compiler.misc.anonymous.class", (Object) null);
+                } else if (interfaces(norm).nonEmpty()) {
+                    s = getLocalizedString(locale, "compiler.misc.anonymous.class",
+                            visit(interfaces(norm).head, locale));
+                } else {
+                    s = getLocalizedString(locale, "compiler.misc.anonymous.class",
+                            visit(supertype(norm), locale));
+                }
+                return s;
+            } else if (longform) {
+                return sym.getQualifiedName().toString();
+            } else {
+                return sym.name.toString();
+            }
+        }
+
+        protected String printMethodArgs(List<Type> args, boolean varArgs, Locale locale) {
+            if (!varArgs) {
+                return visit(args, locale);
+            } else {
+                StringBuffer buf = new StringBuffer();
+                while (args.tail.nonEmpty()) {
+                    buf.append(visit(args.head, locale));
+                    args = args.tail;
+                    buf.append(',');
+                }
+                if (args.head.tag == ARRAY) {
+                    buf.append(visit(((ArrayType) args.head).elemtype, locale));
+                    buf.append("...");
+                } else {
+                    buf.append(visit(args.head, locale));
+                }
+                return buf.toString();
+            }
+        }
+
+        protected String getLocalizedString(Locale locale, String key, Object... args) {
+            return messages.getLocalizedString(key, args);
+        }
+    };
+    // </editor-fold>
+
+    // <editor-fold defaultstate="collapsed" desc="printSymbol">
+    /**
+     * Visitor for generating a string representation of a given symbol
+     * accordingly to a given locale
+     */
+    public String toString(Symbol t, Locale locale) {
+        return symbolPrinter.visit(t, locale);
+    }
+    // where
+    private SymbolPrinter symbolPrinter = new SymbolPrinter();
+
+    public class SymbolPrinter extends DefaultSymbolVisitor<String, Locale> {
+
+        @Override
+        public String visitClassSymbol(ClassSymbol sym, Locale locale) {
+            return sym.name.isEmpty()
+                    ? getLocalizedString(locale, "compiler.misc.anonymous.class", sym.flatname)
+                    : sym.fullname.toString();
+        }
+
+        @Override
+        public String visitMethodSymbol(MethodSymbol s, Locale locale) {
+            if ((s.flags() & BLOCK) != 0) {
+                return s.owner.name.toString();
+            } else {
+                String ms = (s.name == names.init)
+                        ? s.owner.name.toString()
+                        : s.name.toString();
+                if (s.type != null) {
+                    if (s.type.tag == FORALL) {
+                        ms = "<" + typePrinter.visit(s.type.getTypeArguments(), locale) + ">" + ms;
+                    }
+                    ms += "(" + typePrinter.printMethodArgs(
+                            s.type.getParameterTypes(),
+                            (s.flags() & VARARGS) != 0,
+                            locale) + ")";
+                }
+                return ms;
+            }
+        }
+
+        @Override
+        public String visitOperatorSymbol(OperatorSymbol s, Locale locale) {
+            return visitMethodSymbol(s, locale);
+        }
+
+        @Override
+        public String visitPackageSymbol(PackageSymbol s, Locale locale) {
+            return s.name.isEmpty()
+                    ? getLocalizedString(locale, "compiler.misc.unnamed.package")
+                    : s.fullname.toString();
+        }
+
+        @Override
+        public String visitSymbol(Symbol s, Locale locale) {
+            return s.name.toString();
+        }
+
+        public String visit(List<Symbol> ts, Locale locale) {
+            ListBuffer<String> sbuf = lb();
+            for (Symbol t : ts) {
+                sbuf.append(visit(t, locale));
+            }
+            return sbuf.toList().toString();
+        }
+
+        protected String getLocalizedString(Locale locale, String key, Object... args) {
+            return messages.getLocalizedString(key, args);
+        }
+    };
+    // </editor-fold>
+
     // <editor-fold defaultstate="collapsed" desc="toString">
     /**
      * This toString is slightly more descriptive than the one on Type.
+     *
+     * @deprecated Types.toString(Type t, Locale l) provides better support
+     * for localization
      */
+    @Deprecated
     public String toString(Type t) {
         if (t.tag == FORALL) {
             ForAll forAll = (ForAll)t;
@@ -3236,6 +3462,28 @@
     }
 
     /**
+     * A default visitor for symbols.  All visitor methods except
+     * visitSymbol are implemented by delegating to visitSymbol.  Concrete
+     * subclasses must provide an implementation of visitSymbol and can
+     * override other methods as needed.
+     *
+     * @param <R> the return type of the operation implemented by this
+     * visitor; use Void if no return type is needed.
+     * @param <S> the type of the second argument (the first being the
+     * symbol itself) of the operation implemented by this visitor; use
+     * Void if a second argument is not needed.
+     */
+    public static abstract class DefaultSymbolVisitor<R,S> implements Symbol.Visitor<R,S> {
+        final public R visit(Symbol s, S arg)                   { return s.accept(this, arg); }
+        public R visitClassSymbol(ClassSymbol s, S arg)         { return visitSymbol(s, arg); }
+        public R visitMethodSymbol(MethodSymbol s, S arg)       { return visitSymbol(s, arg); }
+        public R visitOperatorSymbol(OperatorSymbol s, S arg)   { return visitSymbol(s, arg); }
+        public R visitPackageSymbol(PackageSymbol s, S arg)     { return visitSymbol(s, arg); }
+        public R visitTypeSymbol(TypeSymbol s, S arg)           { return visitSymbol(s, arg); }
+        public R visitVarSymbol(VarSymbol s, S arg)             { return visitSymbol(s, arg); }
+    }
+
+    /**
      * A <em>simple</em> visitor for types.  This visitor is simple as
      * captured wildcards, for-all types (generic methods), and
      * undetermined type variables (part of inference) are hidden.
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Sep 29 11:34:43 2008 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Sep 29 11:48:09 2008 +0100
@@ -823,6 +823,12 @@
 compiler.misc.anonymous.class=\
     <anonymous {0}>
 
+compiler.misc.type.captureof=\
+    capture#{0} of {1}
+
+compiler.misc.type.none=\
+    <none>
+
 compiler.misc.unnamed.package=\
     unnamed package