ArgTypeCompilerFactory.java revision 2702:b9daa6475f12
1/*
2 * Copyright (c) 2010, 2014, 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
24import java.io.*;
25import java.util.*;
26import java.util.List;
27import javax.tools.*;
28
29import com.sun.tools.javac.api.*;
30import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.DiagnosticPart;
31import com.sun.tools.javac.api.Formattable.LocalizedString;
32import com.sun.tools.javac.code.Flags.Flag;
33import com.sun.tools.javac.code.Kinds.KindName;
34import com.sun.tools.javac.code.*;
35import com.sun.tools.javac.file.*;
36import com.sun.tools.javac.main.Main;
37import com.sun.tools.javac.main.JavaCompiler;
38import com.sun.tools.javac.parser.Tokens.TokenKind;
39import com.sun.tools.javac.util.*;
40import com.sun.tools.javac.util.AbstractDiagnosticFormatter.SimpleConfiguration;
41import javax.lang.model.SourceVersion;
42
43/**
44 * Compiler factory for instances of Example.Compiler that use custom
45 * DiagnosticFormatter and Messages objects to track the types of args
46 * when when localizing diagnostics.
47 * The compiler objects only support "output" mode, not "check" mode.
48 */
49class ArgTypeCompilerFactory implements Example.Compiler.Factory {
50    // Same code as Example.Compiler.DefaultFactory, but the names resolve differently
51    public Example.Compiler getCompiler(List<String> opts, boolean verbose) {
52        String first;
53        String[] rest;
54        if (opts == null || opts.isEmpty()) {
55            first = null;
56            rest = new String[0];
57        } else {
58            first = opts.get(0);
59            rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]);
60        }
61        if (first == null || first.equals("jsr199"))
62            return new Jsr199Compiler(verbose, rest);
63        else if (first.equals("simple"))
64            return new SimpleCompiler(verbose);
65        else if (first.equals("backdoor"))
66            return new BackdoorCompiler(verbose);
67        else
68            throw new IllegalArgumentException(first);
69    }
70
71    /**
72     * Compile using the JSR 199 API.  The diagnostics generated are
73     * scanned for resource keys.   Not all diagnostic keys are generated
74     * via the JSR 199 API -- for example, rich diagnostics are not directly
75     * accessible, and some diagnostics generated by the file manager may
76     * not be generated (for example, the JSR 199 file manager does not see
77     * -Xlint:path).
78     */
79    static class Jsr199Compiler extends Example.Compiler {
80        List<String> fmOpts;
81
82        Jsr199Compiler(boolean verbose, String... args) {
83            super(verbose);
84            for (int i = 0; i < args.length; i++) {
85                String arg = args[i];
86                if (arg.equals("-filemanager") && (i + 1 < args.length)) {
87                    fmOpts = Arrays.asList(args[++i].split(","));
88                } else
89                    throw new IllegalArgumentException(arg);
90            }
91        }
92
93        @Override
94        boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
95            assert out != null && keys == null;
96
97            if (verbose)
98                System.err.println("run_jsr199: " + opts + " " + files);
99
100            JavacTool tool = JavacTool.create();
101
102            StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
103            try {
104                if (fmOpts != null)
105                    fm = new FileManager(fm, fmOpts);
106
107                Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
108
109                Context c = initContext();
110                JavacTaskImpl t = (JavacTaskImpl) tool.getTask(out, fm, null, opts, null, fos, c);
111                return t.call();
112            } finally {
113                close(fm);
114            }
115        }
116    }
117
118
119    /**
120     * Run the test using the standard simple entry point.
121     */
122    static class SimpleCompiler extends Example.Compiler {
123        SimpleCompiler(boolean verbose) {
124            super(verbose);
125        }
126
127        @Override
128        boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
129            assert out != null && keys == null;
130
131            if (verbose)
132                System.err.println("run_simple: " + opts + " " + files);
133
134            List<String> args = new ArrayList<String>();
135
136            args.addAll(opts);
137            for (File f: files)
138                args.add(f.getPath());
139
140            Main main = new Main("javac", out);
141            Context c = initContext();
142            JavacFileManager.preRegister(c); // can't create it until Log has been set up
143
144            try {
145                Main.Result result = main.compile(args.toArray(new String[args.size()]), c);
146
147                return result.isOK();
148            } finally {
149                close(c.get(JavaFileManager.class));
150            }
151        }
152    }
153
154    static class BackdoorCompiler extends Example.Compiler {
155        BackdoorCompiler(boolean verbose) {
156            super(verbose);
157        }
158
159        @Override
160        boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
161            assert out != null && keys == null;
162
163            if (verbose)
164                System.err.println("run_simple: " + opts + " " + files);
165
166            List<String> args = new ArrayList<String>(opts);
167            for (File f: files)
168                args.add(f.getPath());
169
170            Context c = initContext();
171            JavacFileManager.preRegister(c); // can't create it until Log has been set up
172
173            try {
174                Main m = new Main("javac", out);
175                Main.Result result = m.compile(args.toArray(new String[args.size()]), c);
176
177                return result.isOK();
178            } finally {
179                close(c.get(JavaFileManager.class));
180            }
181        }
182    }
183
184    private static Context initContext() {
185        Context context = new Context();
186        ArgTypeMessages.preRegister(context);
187        Options options = Options.instance(context);
188        options.addListener(() -> {
189            Log log = Log.instance(context);
190            log.setDiagnosticFormatter(new ArgTypeDiagnosticFormatter(options));
191        });
192        return context;
193    }
194
195    // <editor-fold defaultstate="collapsed" desc="Custom Javac components">
196
197    /**
198     * Diagnostic formatter which reports formats a diag as a series of lines
199     * containing a key, and a possibly empty set of descriptive strings for the
200     * arg types.
201     */
202    static class ArgTypeDiagnosticFormatter extends AbstractDiagnosticFormatter {
203
204        ArgTypeDiagnosticFormatter(Options options) {
205            super(null, new SimpleConfiguration(options,
206                    EnumSet.of(DiagnosticPart.SUMMARY,
207                    DiagnosticPart.DETAILS,
208                    DiagnosticPart.SUBDIAGNOSTICS)));
209        }
210
211        @Override
212        protected String formatDiagnostic(JCDiagnostic d, Locale locale) {
213            return formatMessage(d, locale);
214        }
215
216        @Override
217        public String formatMessage(JCDiagnostic d, Locale l) {
218            StringBuilder buf = new StringBuilder();
219            formatMessage(d, buf);
220            return buf.toString();
221        }
222
223        private void formatMessage(JCDiagnostic d, StringBuilder buf) {
224            String key = d.getCode();
225            Object[] args = d.getArgs();
226            // report the primary arg types, without recursing into diag fragments
227            buf.append(getKeyArgsString(key, args));
228            // report details for any diagnostic fragments
229            for (Object arg: args) {
230                if (arg instanceof JCDiagnostic) {
231                    buf.append("\n");
232                    formatMessage((JCDiagnostic) arg, buf);
233                }
234            }
235            // report details for any subdiagnostics
236            for (String s: formatSubdiagnostics(d, null)) {
237                buf.append("\n");
238                buf.append(s);
239            }
240        }
241
242        @Override
243        public boolean isRaw() {
244            return true;
245        }
246    }
247
248    /**
249     * Diagnostic formatter which "localizes" a message as a line
250     * containing a key, and a possibly empty set of descriptive strings for the
251     * arg types.
252     */
253    static class ArgTypeMessages extends JavacMessages {
254        static void preRegister(Context context) {
255            context.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() {
256                public JavacMessages make(Context c) {
257                    return new ArgTypeMessages(c) {
258                        @Override
259                        public String getLocalizedString(Locale l, String key, Object... args) {
260                            return getKeyArgsString(key, args);
261                        }
262                    };
263                }
264            });
265        }
266
267        ArgTypeMessages(Context context) {
268            super(context);
269        }
270    }
271
272    /**
273     * Utility method to generate a string for key and args
274     */
275    static String getKeyArgsString(String key, Object... args) {
276        StringBuilder buf = new StringBuilder();
277        buf.append(key);
278        String sep = ": ";
279        for (Object o : args) {
280            buf.append(sep);
281            buf.append(getArgTypeOrStringValue(o));
282            sep = ", ";
283        }
284        return buf.toString();
285    }
286
287    static boolean showStringValues = false;
288
289    static String getArgTypeOrStringValue(Object o) {
290        if (showStringValues && o instanceof String)
291            return "\"" + o + "\"";
292        return getArgType(o);
293    }
294
295    static String getArgType(Object o) {
296        if (o == null)
297            return "null";
298        if (o instanceof Name)
299            return "name";
300        if (o instanceof Boolean)
301            return "boolean";
302        if (o instanceof Integer)
303            return "number";
304        if (o instanceof String)
305            return "string";
306        if (o instanceof Flag)
307            return "modifier";
308        if (o instanceof KindName)
309            return "symbol kind";
310        if (o instanceof TokenKind)
311            return "token";
312        if (o instanceof Symbol)
313            return "symbol";
314        if (o instanceof Type)
315            return "type";
316        if (o instanceof List) {
317            List<?> l = (List<?>) o;
318            if (l.isEmpty())
319                return "list";
320            else
321                return "list of " + getArgType(l.get(0));
322        }
323        if (o instanceof ListBuffer)
324            return getArgType(((ListBuffer) o).toList());
325        if (o instanceof Set) {
326            Set<?> s = (Set<?>) o;
327            if (s.isEmpty())
328                return "set";
329            else
330                return "set of " + getArgType(s.iterator().next());
331        }
332        if (o instanceof SourceVersion)
333            return "source version";
334        if (o instanceof FileObject || o instanceof File)
335            return "file name";
336        if (o instanceof JCDiagnostic)
337            return "message segment";
338        if (o instanceof LocalizedString)
339            return "message segment";  // only instance is "no arguments"
340        String s = o.getClass().getSimpleName();
341        return (s.isEmpty() ? o.getClass().getName() : s);
342    }
343
344    // </editor-fold>
345
346}
347