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