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