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