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