1 /*
   2  * Copyright (c) 2011, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug     7029150 7025809
  27  * @summary Test support for union types
  28  * @library ../../../lib
  29  */
  30 
  31 import java.net.URI;
  32 import java.util.*;
  33 import javax.annotation.processing.*;
  34 import javax.lang.model.element.*;
  35 import javax.lang.model.type.*;
  36 import javax.lang.model.util.*;
  37 import javax.tools.*;
  38 
  39 import com.sun.source.tree.*;
  40 import com.sun.source.util.*;
  41 
  42 public class TestUnionType extends JavacTestingAbstractProcessor {
  43     enum TestKind {
  44         SingleType("E1", "E1",
  45                 "VariableTree: E1 e",
  46                 "VariableTree: elem EXCEPTION_PARAMETER e",
  47                 "VariableTree: elem.type DECLARED",
  48                 "VariableTree: elem.type.elem CLASS E1",
  49                 "VariableTree: type DECLARED",
  50                 "VariableTree: type.elem CLASS E1",
  51                 "VariableTree: type.elem.type DECLARED"),
  52 
  53         ValidTypes("E1, E2", "E1 | E2",
  54                 "VariableTree: E1 | E2 e",
  55                 "VariableTree: elem EXCEPTION_PARAMETER e",
  56                 "VariableTree: elem.type UNION Test.E1,Test.E2",
  57                 "VariableTree: elem.type.elem null",
  58                 "VariableTree: type UNION Test.E1,Test.E2",
  59                 "VariableTree: type.elem null"),
  60 
  61         InvalidTypes("E1, E2", "E1 | EMissing",
  62                 "VariableTree: E1 | EMissing e",
  63                 "VariableTree: elem EXCEPTION_PARAMETER e",
  64                 "VariableTree: elem.type UNION Test.E1,EMissing",
  65                 "VariableTree: elem.type.elem null",
  66                 "VariableTree: type UNION Test.E1,EMissing",
  67                 "VariableTree: type.elem null"),
  68 
  69         Uncaught("E1", "E1 | E2",
  70                 "VariableTree: E1 | E2 e",
  71                 "VariableTree: elem EXCEPTION_PARAMETER e",
  72                 "VariableTree: elem.type UNION Test.E1,Test.E2",
  73                 "VariableTree: elem.type.elem null",
  74                 "VariableTree: type UNION Test.E1,Test.E2",
  75                 "VariableTree: type.elem null");
  76 
  77         TestKind(String throwsTypes, String catchTypes, String... gold) {
  78             this.throwsTypes = throwsTypes;
  79             this.catchTypes = catchTypes;
  80             this.gold = Arrays.asList(gold);
  81         }
  82 
  83         final String throwsTypes;
  84         final String catchTypes;
  85         final List<String> gold;
  86     }
  87 
  88     static class TestFileObject extends SimpleJavaFileObject {
  89         public static final String template =
  90                   "class Test {\n"
  91                 + "    class E1 extends Exception { }\n"
  92                 + "    class E2 extends Exception { }\n"
  93                 + "    void doSomething() throws #T { }\n"
  94                 + "    void test() {\n"
  95                 + "        try {\n"
  96                 + "            doSomething();\n"
  97                 + "        } catch (#C e) {\n"
  98                 + "        }\n"
  99                 + "    }\n"
 100                 + "}\n";
 101 
 102         public TestFileObject(TestKind tk) {
 103             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
 104             this.tk = tk;
 105         }
 106 
 107         @Override
 108         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
 109             return template
 110                     .replace("#T", tk.throwsTypes)
 111                     .replace("#C", tk.catchTypes);
 112         }
 113         final TestKind tk;
 114     }
 115 
 116     public static void main(String... args) throws Exception {
 117         JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
 118         List<String> options = Arrays.asList("-proc:only");
 119         for (TestKind tk: TestKind.values()) {
 120             System.err.println("Test: " + tk);
 121             TestUnionType p = new TestUnionType();
 122             JavaFileObject fo = new TestFileObject(tk);
 123             JavaCompiler.CompilationTask task = comp.getTask(null, null, null, options, null, Arrays.asList(fo));
 124             task.setProcessors(Arrays.asList(p));
 125             boolean ok = task.call();
 126             System.err.println("compilation " + (ok ? "passed" : "failed"));
 127             if (!ok)
 128                 throw new Exception("compilation failed unexpectedly");
 129             if (!p.log.equals(tk.gold)) {
 130                 System.err.println("Expected output:");
 131                 for (String g: tk.gold)
 132                     System.err.println(g);
 133                 throw new Exception("unexpected output from test");
 134             }
 135             System.err.println();
 136         }
 137     }
 138 
 139     Trees trees;
 140     List<String> log;
 141 
 142     @Override
 143     public void init(ProcessingEnvironment env) {
 144         super.init(env);
 145         trees = Trees.instance(env);
 146         log = new ArrayList<String>();
 147     }
 148 
 149     @Override
 150     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 151         if (!roundEnv.processingOver()) {
 152             for (Element e: roundEnv.getRootElements()) {
 153                 scan(trees.getPath(e));
 154             }
 155         }
 156         return true;
 157     }
 158 
 159     void scan(TreePath path) {
 160         new Scanner().scan(path, null);
 161     }
 162 
 163     class Scanner extends TreePathScanner<Void,Void> {
 164         @Override
 165         public Void visitVariable(VariableTree tree, Void ignore) {
 166             TreePath p = getCurrentPath();
 167             Element e = trees.getElement(p);
 168             if (e.getKind() == ElementKind.EXCEPTION_PARAMETER) {
 169                 log("VariableTree: " + tree);
 170                 log("VariableTree: elem " + print(e));
 171                 log("VariableTree: elem.type " + print(e.asType()));
 172                 log("VariableTree: elem.type.elem " + print(types.asElement(e.asType())));
 173                 TypeMirror tm = trees.getTypeMirror(p);
 174                 log("VariableTree: type " + print(tm));
 175                 log("VariableTree: type.elem " + print(types.asElement(tm)));
 176                 if (types.asElement(tm) != null)
 177                     log("VariableTree: type.elem.type " + print(types.asElement(tm).asType()));
 178             }
 179             return super.visitVariable(tree, null);
 180         }
 181 
 182         String print(TypeMirror tm) {
 183             return (tm == null) ? null : new TypePrinter().visit(tm);
 184         }
 185 
 186         String print(Element e) {
 187             return (e == null) ? null : (e.getKind() + " " + e.getSimpleName());
 188         }
 189 
 190         void log(String msg) {
 191             System.err.println(msg);
 192             log.add(msg);
 193         }
 194     }
 195 
 196     class TypePrinter extends SimpleTypeVisitor<String, Void> {
 197         @Override
 198         protected String defaultAction(TypeMirror tm, Void ignore) {
 199             return String.valueOf(tm.getKind());
 200         }
 201 
 202         @Override
 203         public String visitUnion(UnionType t, Void ignore) {
 204             return (t.getKind() + " " + t.getAlternatives());
 205         }
 206     }
 207 }