ClientCodeWrapper.java revision 2601:8e638f046bf0
1/*
2 * Copyright (c) 2011, 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.  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
26
27package com.sun.tools.javac.api;
28
29import java.io.IOException;
30import java.io.InputStream;
31import java.io.OutputStream;
32import java.io.Reader;
33import java.io.Writer;
34import java.lang.annotation.ElementType;
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37import java.lang.annotation.Target;
38import java.net.URI;
39import java.util.ArrayList;
40import java.util.Collection;
41import java.util.Collections;
42import java.util.HashMap;
43import java.util.Iterator;
44import java.util.List;
45import java.util.Locale;
46import java.util.Map;
47import java.util.Set;
48
49import javax.lang.model.element.Modifier;
50import javax.lang.model.element.NestingKind;
51import javax.tools.Diagnostic;
52import javax.tools.DiagnosticListener;
53import javax.tools.FileObject;
54import javax.tools.JavaFileManager;
55import javax.tools.JavaFileManager.Location;
56import javax.tools.JavaFileObject;
57import javax.tools.JavaFileObject.Kind;
58
59import com.sun.source.util.TaskEvent;
60import com.sun.source.util.TaskListener;
61import com.sun.tools.javac.util.ClientCodeException;
62import com.sun.tools.javac.util.Context;
63import com.sun.tools.javac.util.DefinedBy;
64import com.sun.tools.javac.util.DefinedBy.Api;
65import com.sun.tools.javac.util.JCDiagnostic;
66
67/**
68 *  Wrap objects to enable unchecked exceptions to be caught and handled.
69 *
70 *  For each method, exceptions are handled as follows:
71 *  <ul>
72 *  <li>Checked exceptions are left alone and propogate upwards in the
73 *      obvious way, since they are an expected aspect of the method's
74 *      specification.
75 *  <li>Unchecked exceptions which have already been caught and wrapped in
76 *      ClientCodeException are left alone to continue propogating upwards.
77 *  <li>All other unchecked exceptions (i.e. subtypes of RuntimeException
78 *      and Error) and caught, and rethrown as a ClientCodeException with
79 *      its cause set to the original exception.
80 *  </ul>
81 *
82 *  The intent is that ClientCodeException can be caught at an appropriate point
83 *  in the program and can be distinguished from any unanticipated unchecked
84 *  exceptions arising in the main body of the code (i.e. bugs.) When the
85 *  ClientCodeException has been caught, either a suitable message can be
86 *  generated, or if appropriate, the original cause can be rethrown.
87 *
88 *  <p><b>This is NOT part of any supported API.
89 *  If you write code that depends on this, you do so at your own risk.
90 *  This code and its internal interfaces are subject to change or
91 *  deletion without notice.</b>
92 */
93public class ClientCodeWrapper {
94    @Retention(RetentionPolicy.RUNTIME)
95    @Target(ElementType.TYPE)
96    public @interface Trusted { }
97
98    public static ClientCodeWrapper instance(Context context) {
99        ClientCodeWrapper instance = context.get(ClientCodeWrapper.class);
100        if (instance == null)
101            instance = new ClientCodeWrapper(context);
102        return instance;
103    }
104
105    /**
106     * A map to cache the results of whether or not a specific classes can
107     * be "trusted", and thus does not need to be wrapped.
108     */
109    Map<Class<?>, Boolean> trustedClasses;
110
111    protected ClientCodeWrapper(Context context) {
112        trustedClasses = new HashMap<>();
113    }
114
115    public JavaFileManager wrap(JavaFileManager fm) {
116        if (isTrusted(fm))
117            return fm;
118        return new WrappedJavaFileManager(fm);
119    }
120
121    public FileObject wrap(FileObject fo) {
122        if (isTrusted(fo))
123            return fo;
124        return new WrappedFileObject(fo);
125    }
126
127    FileObject unwrap(FileObject fo) {
128        if (fo instanceof WrappedFileObject)
129            return ((WrappedFileObject) fo).clientFileObject;
130        else
131            return fo;
132    }
133
134    public JavaFileObject wrap(JavaFileObject fo) {
135        if (isTrusted(fo))
136            return fo;
137        return new WrappedJavaFileObject(fo);
138    }
139
140    public Iterable<JavaFileObject> wrapJavaFileObjects(Iterable<? extends JavaFileObject> list) {
141        List<JavaFileObject> wrapped = new ArrayList<>();
142        for (JavaFileObject fo : list)
143            wrapped.add(wrap(fo));
144        return Collections.unmodifiableList(wrapped);
145    }
146
147    JavaFileObject unwrap(JavaFileObject fo) {
148        if (fo instanceof WrappedJavaFileObject)
149            return ((JavaFileObject) ((WrappedJavaFileObject) fo).clientFileObject);
150        else
151            return fo;
152    }
153
154    public <T /*super JavaFileOject*/> DiagnosticListener<T> wrap(DiagnosticListener<T> dl) {
155        if (isTrusted(dl))
156            return dl;
157        return new WrappedDiagnosticListener<>(dl);
158    }
159
160    TaskListener wrap(TaskListener tl) {
161        if (isTrusted(tl))
162            return tl;
163        return new WrappedTaskListener(tl);
164    }
165
166    TaskListener unwrap(TaskListener l) {
167        if (l instanceof WrappedTaskListener)
168            return ((WrappedTaskListener) l).clientTaskListener;
169        else
170            return l;
171    }
172
173    Collection<TaskListener> unwrap(Collection<? extends TaskListener> listeners) {
174        Collection<TaskListener> c = new ArrayList<>(listeners.size());
175        for (TaskListener l: listeners)
176            c.add(unwrap(l));
177        return c;
178    }
179
180    @SuppressWarnings("unchecked")
181    private <T> Diagnostic<T> unwrap(final Diagnostic<T> diagnostic) {
182        if (diagnostic instanceof JCDiagnostic) {
183            JCDiagnostic d = (JCDiagnostic) diagnostic;
184            return (Diagnostic<T>) new DiagnosticSourceUnwrapper(d);
185        } else {
186            return diagnostic;
187        }
188    }
189
190    protected boolean isTrusted(Object o) {
191        Class<?> c = o.getClass();
192        Boolean trusted = trustedClasses.get(c);
193        if (trusted == null) {
194            trusted = c.getName().startsWith("com.sun.tools.javac.")
195                    || c.isAnnotationPresent(Trusted.class);
196            trustedClasses.put(c, trusted);
197        }
198        return trusted;
199    }
200
201    private String wrappedToString(Class<?> wrapperClass, Object wrapped) {
202        return wrapperClass.getSimpleName() + "[" + wrapped + "]";
203    }
204
205    // <editor-fold defaultstate="collapsed" desc="Wrapper classes">
206
207    protected class WrappedJavaFileManager implements JavaFileManager {
208        protected JavaFileManager clientJavaFileManager;
209        WrappedJavaFileManager(JavaFileManager clientJavaFileManager) {
210            clientJavaFileManager.getClass(); // null check
211            this.clientJavaFileManager = clientJavaFileManager;
212        }
213
214        @Override @DefinedBy(Api.COMPILER)
215        public ClassLoader getClassLoader(Location location) {
216            try {
217                return clientJavaFileManager.getClassLoader(location);
218            } catch (ClientCodeException e) {
219                throw e;
220            } catch (RuntimeException | Error e) {
221                throw new ClientCodeException(e);
222            }
223        }
224
225        @Override @DefinedBy(Api.COMPILER)
226        public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
227            try {
228                return wrapJavaFileObjects(clientJavaFileManager.list(location, packageName, kinds, recurse));
229            } catch (ClientCodeException e) {
230                throw e;
231            } catch (RuntimeException | Error e) {
232                throw new ClientCodeException(e);
233            }
234        }
235
236        @Override @DefinedBy(Api.COMPILER)
237        public String inferBinaryName(Location location, JavaFileObject file) {
238            try {
239                return clientJavaFileManager.inferBinaryName(location, unwrap(file));
240            } catch (ClientCodeException e) {
241                throw e;
242            } catch (RuntimeException | Error e) {
243                throw new ClientCodeException(e);
244            }
245        }
246
247        @Override @DefinedBy(Api.COMPILER)
248        public boolean isSameFile(FileObject a, FileObject b) {
249            try {
250                return clientJavaFileManager.isSameFile(unwrap(a), unwrap(b));
251            } catch (ClientCodeException e) {
252                throw e;
253            } catch (RuntimeException | Error e) {
254                throw new ClientCodeException(e);
255            }
256        }
257
258        @Override @DefinedBy(Api.COMPILER)
259        public boolean handleOption(String current, Iterator<String> remaining) {
260            try {
261                return clientJavaFileManager.handleOption(current, remaining);
262            } catch (ClientCodeException e) {
263                throw e;
264            } catch (RuntimeException | Error e) {
265                throw new ClientCodeException(e);
266            }
267        }
268
269        @Override @DefinedBy(Api.COMPILER)
270        public boolean hasLocation(Location location) {
271            try {
272                return clientJavaFileManager.hasLocation(location);
273            } catch (ClientCodeException e) {
274                throw e;
275            } catch (RuntimeException | Error e) {
276                throw new ClientCodeException(e);
277            }
278        }
279
280        @Override @DefinedBy(Api.COMPILER)
281        public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
282            try {
283                return wrap(clientJavaFileManager.getJavaFileForInput(location, className, kind));
284            } catch (ClientCodeException e) {
285                throw e;
286            } catch (RuntimeException | Error e) {
287                throw new ClientCodeException(e);
288            }
289        }
290
291        @Override @DefinedBy(Api.COMPILER)
292        public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
293            try {
294                return wrap(clientJavaFileManager.getJavaFileForOutput(location, className, kind, unwrap(sibling)));
295            } catch (ClientCodeException e) {
296                throw e;
297            } catch (RuntimeException | Error e) {
298                throw new ClientCodeException(e);
299            }
300        }
301
302        @Override @DefinedBy(Api.COMPILER)
303        public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
304            try {
305                return wrap(clientJavaFileManager.getFileForInput(location, packageName, relativeName));
306            } catch (ClientCodeException e) {
307                throw e;
308            } catch (RuntimeException | Error e) {
309                throw new ClientCodeException(e);
310            }
311        }
312
313        @Override @DefinedBy(Api.COMPILER)
314        public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
315            try {
316                return wrap(clientJavaFileManager.getFileForOutput(location, packageName, relativeName, unwrap(sibling)));
317            } catch (ClientCodeException e) {
318                throw e;
319            } catch (RuntimeException | Error e) {
320                throw new ClientCodeException(e);
321            }
322        }
323
324        @Override @DefinedBy(Api.COMPILER)
325        public void flush() throws IOException {
326            try {
327                clientJavaFileManager.flush();
328            } catch (ClientCodeException e) {
329                throw e;
330            } catch (RuntimeException | Error e) {
331                throw new ClientCodeException(e);
332            }
333        }
334
335        @Override @DefinedBy(Api.COMPILER)
336        public void close() throws IOException {
337            try {
338                clientJavaFileManager.close();
339            } catch (ClientCodeException e) {
340                throw e;
341            } catch (RuntimeException | Error e) {
342                throw new ClientCodeException(e);
343            }
344        }
345
346        @Override @DefinedBy(Api.COMPILER)
347        public int isSupportedOption(String option) {
348            try {
349                return clientJavaFileManager.isSupportedOption(option);
350            } catch (ClientCodeException e) {
351                throw e;
352            } catch (RuntimeException | Error e) {
353                throw new ClientCodeException(e);
354            }
355        }
356
357        @Override
358        public String toString() {
359            return wrappedToString(getClass(), clientJavaFileManager);
360        }
361    }
362
363    protected class WrappedFileObject implements FileObject {
364        protected FileObject clientFileObject;
365        WrappedFileObject(FileObject clientFileObject) {
366            clientFileObject.getClass(); // null check
367            this.clientFileObject = clientFileObject;
368        }
369
370        @Override @DefinedBy(Api.COMPILER)
371        public URI toUri() {
372            try {
373                return clientFileObject.toUri();
374            } catch (ClientCodeException e) {
375                throw e;
376            } catch (RuntimeException | Error e) {
377                throw new ClientCodeException(e);
378            }
379        }
380
381        @Override @DefinedBy(Api.COMPILER)
382        public String getName() {
383            try {
384                return clientFileObject.getName();
385            } catch (ClientCodeException e) {
386                throw e;
387            } catch (RuntimeException | Error e) {
388                throw new ClientCodeException(e);
389            }
390        }
391
392        @Override @DefinedBy(Api.COMPILER)
393        public InputStream openInputStream() throws IOException {
394            try {
395                return clientFileObject.openInputStream();
396            } catch (ClientCodeException e) {
397                throw e;
398            } catch (RuntimeException | Error e) {
399                throw new ClientCodeException(e);
400            }
401        }
402
403        @Override @DefinedBy(Api.COMPILER)
404        public OutputStream openOutputStream() throws IOException {
405            try {
406                return clientFileObject.openOutputStream();
407            } catch (ClientCodeException e) {
408                throw e;
409            } catch (RuntimeException | Error e) {
410                throw new ClientCodeException(e);
411            }
412        }
413
414        @Override @DefinedBy(Api.COMPILER)
415        public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
416            try {
417                return clientFileObject.openReader(ignoreEncodingErrors);
418            } catch (ClientCodeException e) {
419                throw e;
420            } catch (RuntimeException | Error e) {
421                throw new ClientCodeException(e);
422            }
423        }
424
425        @Override @DefinedBy(Api.COMPILER)
426        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
427            try {
428                return clientFileObject.getCharContent(ignoreEncodingErrors);
429            } catch (ClientCodeException e) {
430                throw e;
431            } catch (RuntimeException | Error e) {
432                throw new ClientCodeException(e);
433            }
434        }
435
436        @Override @DefinedBy(Api.COMPILER)
437        public Writer openWriter() throws IOException {
438            try {
439                return clientFileObject.openWriter();
440            } catch (ClientCodeException e) {
441                throw e;
442            } catch (RuntimeException | Error e) {
443                throw new ClientCodeException(e);
444            }
445        }
446
447        @Override @DefinedBy(Api.COMPILER)
448        public long getLastModified() {
449            try {
450                return clientFileObject.getLastModified();
451            } catch (ClientCodeException e) {
452                throw e;
453            } catch (RuntimeException | Error e) {
454                throw new ClientCodeException(e);
455            }
456        }
457
458        @Override @DefinedBy(Api.COMPILER)
459        public boolean delete() {
460            try {
461                return clientFileObject.delete();
462            } catch (ClientCodeException e) {
463                throw e;
464            } catch (RuntimeException | Error e) {
465                throw new ClientCodeException(e);
466            }
467        }
468
469        @Override
470        public String toString() {
471            return wrappedToString(getClass(), clientFileObject);
472        }
473    }
474
475    protected class WrappedJavaFileObject extends WrappedFileObject implements JavaFileObject {
476        WrappedJavaFileObject(JavaFileObject clientJavaFileObject) {
477            super(clientJavaFileObject);
478        }
479
480        @Override @DefinedBy(Api.COMPILER)
481        public Kind getKind() {
482            try {
483                return ((JavaFileObject)clientFileObject).getKind();
484            } catch (ClientCodeException e) {
485                throw e;
486            } catch (RuntimeException | Error e) {
487                throw new ClientCodeException(e);
488            }
489        }
490
491        @Override @DefinedBy(Api.COMPILER)
492        public boolean isNameCompatible(String simpleName, Kind kind) {
493            try {
494                return ((JavaFileObject)clientFileObject).isNameCompatible(simpleName, kind);
495            } catch (ClientCodeException e) {
496                throw e;
497            } catch (RuntimeException | Error e) {
498                throw new ClientCodeException(e);
499            }
500        }
501
502        @Override @DefinedBy(Api.COMPILER)
503        public NestingKind getNestingKind() {
504            try {
505                return ((JavaFileObject)clientFileObject).getNestingKind();
506            } catch (ClientCodeException e) {
507                throw e;
508            } catch (RuntimeException | Error e) {
509                throw new ClientCodeException(e);
510            }
511        }
512
513        @Override @DefinedBy(Api.COMPILER)
514        public Modifier getAccessLevel() {
515            try {
516                return ((JavaFileObject)clientFileObject).getAccessLevel();
517            } catch (ClientCodeException e) {
518                throw e;
519            } catch (RuntimeException | Error e) {
520                throw new ClientCodeException(e);
521            }
522        }
523
524        @Override
525        public String toString() {
526            return wrappedToString(getClass(), clientFileObject);
527        }
528    }
529
530    protected class WrappedDiagnosticListener<T /*super JavaFileObject*/> implements DiagnosticListener<T> {
531        protected DiagnosticListener<T> clientDiagnosticListener;
532        WrappedDiagnosticListener(DiagnosticListener<T> clientDiagnosticListener) {
533            clientDiagnosticListener.getClass(); // null check
534            this.clientDiagnosticListener = clientDiagnosticListener;
535        }
536
537        @Override @DefinedBy(Api.COMPILER)
538        public void report(Diagnostic<? extends T> diagnostic) {
539            try {
540                clientDiagnosticListener.report(unwrap(diagnostic));
541            } catch (ClientCodeException e) {
542                throw e;
543            } catch (RuntimeException | Error e) {
544                throw new ClientCodeException(e);
545            }
546        }
547
548        @Override
549        public String toString() {
550            return wrappedToString(getClass(), clientDiagnosticListener);
551        }
552    }
553
554    public class DiagnosticSourceUnwrapper implements Diagnostic<JavaFileObject> {
555        public final JCDiagnostic d;
556
557        DiagnosticSourceUnwrapper(JCDiagnostic d) {
558            this.d = d;
559        }
560
561        @DefinedBy(Api.COMPILER)
562        public Diagnostic.Kind getKind() {
563            return d.getKind();
564        }
565
566        @DefinedBy(Api.COMPILER)
567        public JavaFileObject getSource() {
568            return unwrap(d.getSource());
569        }
570
571        @DefinedBy(Api.COMPILER)
572        public long getPosition() {
573            return d.getPosition();
574        }
575
576        @DefinedBy(Api.COMPILER)
577        public long getStartPosition() {
578            return d.getStartPosition();
579        }
580
581        @DefinedBy(Api.COMPILER)
582        public long getEndPosition() {
583            return d.getEndPosition();
584        }
585
586        @DefinedBy(Api.COMPILER)
587        public long getLineNumber() {
588            return d.getLineNumber();
589        }
590
591        @DefinedBy(Api.COMPILER)
592        public long getColumnNumber() {
593            return d.getColumnNumber();
594        }
595
596        @DefinedBy(Api.COMPILER)
597        public String getCode() {
598            return d.getCode();
599        }
600
601        @DefinedBy(Api.COMPILER)
602        public String getMessage(Locale locale) {
603            return d.getMessage(locale);
604        }
605
606        @Override
607        public String toString() {
608            return d.toString();
609        }
610    }
611
612    protected class WrappedTaskListener implements TaskListener {
613        protected TaskListener clientTaskListener;
614        WrappedTaskListener(TaskListener clientTaskListener) {
615            clientTaskListener.getClass(); // null check
616            this.clientTaskListener = clientTaskListener;
617        }
618
619        @Override @DefinedBy(Api.COMPILER_TREE)
620        public void started(TaskEvent ev) {
621            try {
622                clientTaskListener.started(ev);
623            } catch (ClientCodeException e) {
624                throw e;
625            } catch (RuntimeException | Error e) {
626                throw new ClientCodeException(e);
627            }
628        }
629
630        @Override @DefinedBy(Api.COMPILER_TREE)
631        public void finished(TaskEvent ev) {
632            try {
633                clientTaskListener.finished(ev);
634            } catch (ClientCodeException e) {
635                throw e;
636            } catch (RuntimeException | Error e) {
637                throw new ClientCodeException(e);
638            }
639        }
640
641        @Override
642        public String toString() {
643            return wrappedToString(getClass(), clientTaskListener);
644        }
645    }
646
647    // </editor-fold>
648}
649