1/*
2 * Copyright (c) 2011, 2015, 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 /tools/javac/lib
29 * @modules jdk.compiler
30 */
31
32import java.net.URI;
33import java.util.*;
34import javax.annotation.processing.*;
35import javax.lang.model.element.*;
36import javax.lang.model.type.*;
37import javax.lang.model.util.*;
38import javax.tools.*;
39
40import com.sun.source.tree.*;
41import com.sun.source.util.*;
42
43public class TestUnionType extends JavacTestingAbstractProcessor {
44    enum TestKind {
45        SingleType("E1", "E1",
46                "VariableTree: E1 e",
47                "VariableTree: elem EXCEPTION_PARAMETER e",
48                "VariableTree: elem.type DECLARED",
49                "VariableTree: elem.type.elem CLASS E1",
50                "VariableTree: type DECLARED",
51                "VariableTree: type.elem CLASS E1",
52                "VariableTree: type.elem.type DECLARED"),
53
54        ValidTypes("E1, E2", "E1 | E2",
55                "VariableTree: E1 | E2 e",
56                "VariableTree: elem EXCEPTION_PARAMETER e",
57                "VariableTree: elem.type UNION Test.E1,Test.E2",
58                "VariableTree: elem.type.elem null",
59                "VariableTree: type UNION Test.E1,Test.E2",
60                "VariableTree: type.elem null"),
61
62        InvalidTypes("E1, E2", "E1 | EMissing",
63                "VariableTree: E1 | EMissing e",
64                "VariableTree: elem EXCEPTION_PARAMETER e",
65                "VariableTree: elem.type UNION Test.E1,EMissing",
66                "VariableTree: elem.type.elem null",
67                "VariableTree: type UNION Test.E1,EMissing",
68                "VariableTree: type.elem null"),
69
70        Uncaught("E1", "E1 | E2",
71                "VariableTree: E1 | E2 e",
72                "VariableTree: elem EXCEPTION_PARAMETER e",
73                "VariableTree: elem.type UNION Test.E1,Test.E2",
74                "VariableTree: elem.type.elem null",
75                "VariableTree: type UNION Test.E1,Test.E2",
76                "VariableTree: type.elem null");
77
78        TestKind(String throwsTypes, String catchTypes, String... gold) {
79            this.throwsTypes = throwsTypes;
80            this.catchTypes = catchTypes;
81            this.gold = Arrays.asList(gold);
82        }
83
84        final String throwsTypes;
85        final String catchTypes;
86        final List<String> gold;
87    }
88
89    static class TestFileObject extends SimpleJavaFileObject {
90        public static final String template =
91                  "class Test {\n"
92                + "    class E1 extends Exception { }\n"
93                + "    class E2 extends Exception { }\n"
94                + "    void doSomething() throws #T { }\n"
95                + "    void test() {\n"
96                + "        try {\n"
97                + "            doSomething();\n"
98                + "        } catch (#C e) {\n"
99                + "        }\n"
100                + "    }\n"
101                + "}\n";
102
103        public TestFileObject(TestKind tk) {
104            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
105            this.tk = tk;
106        }
107
108        @Override
109        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
110            return template
111                    .replace("#T", tk.throwsTypes)
112                    .replace("#C", tk.catchTypes);
113        }
114        final TestKind tk;
115    }
116
117    public static void main(String... args) throws Exception {
118        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
119        List<String> options = Arrays.asList("-proc:only");
120        for (TestKind tk: TestKind.values()) {
121            System.err.println("Test: " + tk);
122            TestUnionType p = new TestUnionType();
123            JavaFileObject fo = new TestFileObject(tk);
124            JavaCompiler.CompilationTask task = comp.getTask(null, null, null, options, null, Arrays.asList(fo));
125            task.setProcessors(Arrays.asList(p));
126            boolean ok = task.call();
127            System.err.println("compilation " + (ok ? "passed" : "failed"));
128            if (!ok)
129                throw new Exception("compilation failed unexpectedly");
130            if (!p.log.equals(tk.gold)) {
131                System.err.println("Expected output:");
132                for (String g: tk.gold)
133                    System.err.println(g);
134                throw new Exception("unexpected output from test");
135            }
136            System.err.println();
137        }
138    }
139
140    Trees trees;
141    List<String> log;
142
143    @Override
144    public void init(ProcessingEnvironment env) {
145        super.init(env);
146        trees = Trees.instance(env);
147        log = new ArrayList<String>();
148    }
149
150    @Override
151    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
152        if (!roundEnv.processingOver()) {
153            for (Element e: roundEnv.getRootElements()) {
154                scan(trees.getPath(e));
155            }
156        }
157        return true;
158    }
159
160    void scan(TreePath path) {
161        new Scanner().scan(path, null);
162    }
163
164    class Scanner extends TreePathScanner<Void,Void> {
165        @Override
166        public Void visitVariable(VariableTree tree, Void ignore) {
167            TreePath p = getCurrentPath();
168            Element e = trees.getElement(p);
169            if (e.getKind() == ElementKind.EXCEPTION_PARAMETER) {
170                log("VariableTree: " + tree);
171                log("VariableTree: elem " + print(e));
172                log("VariableTree: elem.type " + print(e.asType()));
173                log("VariableTree: elem.type.elem " + print(types.asElement(e.asType())));
174                TypeMirror tm = trees.getTypeMirror(p);
175                log("VariableTree: type " + print(tm));
176                log("VariableTree: type.elem " + print(types.asElement(tm)));
177                if (types.asElement(tm) != null)
178                    log("VariableTree: type.elem.type " + print(types.asElement(tm).asType()));
179            }
180            return super.visitVariable(tree, null);
181        }
182
183        String print(TypeMirror tm) {
184            return (tm == null) ? null : new TypePrinter().visit(tm);
185        }
186
187        String print(Element e) {
188            return (e == null) ? null : (e.getKind() + " " + e.getSimpleName());
189        }
190
191        void log(String msg) {
192            System.err.println(msg);
193            log.add(msg);
194        }
195    }
196
197    class TypePrinter extends SimpleTypeVisitor<String, Void> {
198        @Override
199        protected String defaultAction(TypeMirror tm, Void ignore) {
200            return String.valueOf(tm.getKind());
201        }
202
203        @Override
204        public String visitUnion(UnionType t, Void ignore) {
205            return (t.getKind() + " " + t.getAlternatives());
206        }
207    }
208}
209