1 /*
   2  * Copyright (c) 2015, 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 8072480 8203814
  27  * @summary Check the platform classpath contains the correct elements.
  28  * @library /tools/lib
  29  * @modules jdk.compiler/com.sun.tools.javac.code
  30  *          jdk.compiler/com.sun.tools.javac.api
  31  *          jdk.compiler/com.sun.tools.javac.main
  32  *          jdk.compiler/com.sun.tools.javac.platform
  33  *          jdk.compiler/com.sun.tools.javac.util
  34  *          jdk.jdeps/com.sun.tools.classfile
  35  *          jdk.jdeps/com.sun.tools.javap
  36  * @build toolbox.ToolBox ElementStructureTest
  37  * @run main ElementStructureTest
  38  */
  39 
  40 import java.io.BufferedReader;
  41 import java.io.ByteArrayInputStream;
  42 import java.io.ByteArrayOutputStream;
  43 import java.io.File;
  44 import java.io.IOException;
  45 import java.io.InputStream;
  46 import java.io.OutputStream;
  47 import java.io.OutputStreamWriter;
  48 import java.io.Reader;
  49 import java.io.Writer;
  50 import java.net.URI;
  51 import java.net.URL;
  52 import java.net.URLClassLoader;
  53 import java.nio.file.Files;
  54 import java.nio.file.Path;
  55 import java.nio.file.Paths;
  56 import java.security.MessageDigest;
  57 import java.util.ArrayList;
  58 import java.util.Arrays;
  59 import java.util.Collections;
  60 import java.util.EnumSet;
  61 import java.util.HashMap;
  62 import java.util.Iterator;
  63 import java.util.List;
  64 import java.util.Map;
  65 import java.util.Map.Entry;
  66 import java.util.ServiceLoader;
  67 import java.util.Set;
  68 import java.util.TreeMap;
  69 import java.util.TreeSet;
  70 import java.util.function.Predicate;
  71 import java.util.regex.Pattern;
  72 import java.util.stream.Stream;
  73 
  74 import javax.lang.model.element.AnnotationMirror;
  75 import javax.lang.model.element.AnnotationValue;
  76 import javax.lang.model.element.Element;
  77 import javax.lang.model.element.ElementVisitor;
  78 import javax.lang.model.element.ExecutableElement;
  79 import javax.lang.model.element.Modifier;
  80 import javax.lang.model.element.ModuleElement;
  81 import javax.lang.model.element.NestingKind;
  82 import javax.lang.model.element.PackageElement;
  83 import javax.lang.model.element.TypeElement;
  84 import javax.lang.model.element.TypeParameterElement;
  85 import javax.lang.model.element.VariableElement;
  86 import javax.lang.model.type.TypeMirror;
  87 import javax.tools.FileObject;
  88 import javax.tools.JavaCompiler;
  89 import javax.tools.JavaFileManager;
  90 import javax.tools.JavaFileObject;
  91 import javax.tools.JavaFileObject.Kind;
  92 import javax.tools.StandardLocation;
  93 import javax.tools.ToolProvider;
  94 
  95 import com.sun.source.util.JavacTask;
  96 import com.sun.tools.classfile.ClassFile;
  97 import com.sun.tools.classfile.ConstantPoolException;
  98 import com.sun.tools.javac.api.JavacTaskImpl;
  99 import com.sun.tools.javac.code.Symbol.CompletionFailure;
 100 import com.sun.tools.javac.platform.PlatformProvider;
 101 
 102 import toolbox.ToolBox;
 103 
 104 
 105 /**To generate the hash values for version N, invoke this class like:
 106  *
 107  *     java ElementStructureTest generate-hashes $LANGTOOLS_DIR/make/data/symbols/include.list (<classes-for-N> N)+
 108  *
 109  * Where <classes-for-N> is the file produced by make/src/classes/build/tools/symbolgenerator/Probe.java.
 110  * So, to produce hashes for 6, 7 and 8, this command can be used:
 111  *
 112  *     java ElementStructureTest generate-hashes classes-6 6 classes-7 7 classes-8 8
 113  *
 114  * To inspect differences between the actual and expected output for version N, invoke this class like:
 115  *
 116  *     java ElementStructureTest generate-output $LANGTOOLS_DIR/make/data/symbols/include.list (<classes-for-N> N <actual-output-file> <expected-output-file>)+
 117  *
 118  * For example, to get the actual and expected output for 6 in /tmp/actual and /tmp/expected, respectively:
 119  *
 120  *     java ElementStructureTest generate-output $LANGTOOLS_DIR/make/data/symbols/include.list classes-6 6 /tmp/actual /tmp/expected
 121  */
 122 public class ElementStructureTest {
 123 
 124     static final byte[] hash6 = new byte[] {
 125         (byte) 0x99, (byte) 0x34, (byte) 0x82, (byte) 0xCF,
 126         (byte) 0xE0, (byte) 0x53, (byte) 0xF3, (byte) 0x13,
 127         (byte) 0x4E, (byte) 0xCF, (byte) 0x49, (byte) 0x32,
 128         (byte) 0xB7, (byte) 0x52, (byte) 0x0F, (byte) 0x68
 129     };
 130     static final byte[] hash7 = new byte[] {
 131         (byte) 0x3C, (byte) 0x03, (byte) 0xEA, (byte) 0x4A,
 132         (byte) 0x62, (byte) 0xD2, (byte) 0x18, (byte) 0xE5,
 133         (byte) 0xA5, (byte) 0xC2, (byte) 0xB7, (byte) 0x85,
 134         (byte) 0x90, (byte) 0xFA, (byte) 0x98, (byte) 0xCD
 135     };
 136     static final byte[] hash8 = new byte[] {
 137         (byte) 0x0B, (byte) 0xEB, (byte) 0x16, (byte) 0xF5,
 138         (byte) 0x7F, (byte) 0xB0, (byte) 0x18, (byte) 0xF1,
 139         (byte) 0x78, (byte) 0x11, (byte) 0xED, (byte) 0x30,
 140         (byte) 0x19, (byte) 0x4D, (byte) 0xDE, (byte) 0x8A
 141     };
 142 
 143     final static Map<String, byte[]> version2Hash = new HashMap<>();
 144 
 145     static {
 146         version2Hash.put("6", hash6);
 147         version2Hash.put("7", hash7);
 148         version2Hash.put("8", hash8);
 149     }
 150 
 151     public static void main(String... args) throws Exception {
 152         if (args.length == 0) {
 153             new ElementStructureTest().doTest();
 154             return ;
 155         }
 156         switch (args[0]) {
 157             case "generate-hashes":
 158                 new ElementStructureTest().generateHashes(args);
 159                 break;
 160             case "generate-output":
 161                 new ElementStructureTest().generateOutput(args);
 162                 break;
 163             default:
 164                 throw new IllegalStateException("Unrecognized request: " + args[0]);
 165         }
 166     }
 167 
 168     void doTest() throws Exception {
 169         for (PlatformProvider provider : ServiceLoader.load(PlatformProvider.class)) {
 170             for (String ver : provider.getSupportedPlatformNames()) {
 171                 if (!version2Hash.containsKey(ver))
 172                     continue;
 173                 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); Writer output = new OutputStreamWriter(baos, "UTF-8")) {
 174                     run(output, ver);
 175                     output.close();
 176                     byte[] actual = MessageDigest.getInstance("MD5").digest(baos.toByteArray());
 177                     if (!Arrays.equals(version2Hash.get(ver), actual))
 178                         throw new AssertionError("Wrong hash: " + toHex(actual) + " for version: " + ver);
 179                 }
 180             }
 181         }
 182     }
 183 
 184     void generateHashes(String... args) throws Exception {
 185         Predicate<String> ignoreList = constructAcceptIgnoreList(args[1]);
 186         for (int i = 2; i < args.length; i += 2) {
 187             try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); Writer output = new OutputStreamWriter(baos, "UTF-8")) {
 188                 realClasses(args[i], ignoreList, output, args[i + 1]);
 189                 output.close();
 190                 System.err.println("version:" + args[i + 1] + "; " + toHex(MessageDigest.getInstance("MD5").digest(baos.toByteArray())));
 191             }
 192         }
 193     }
 194 
 195     void generateOutput(String... args) throws Exception {
 196         Predicate<String> ignoreList = constructAcceptIgnoreList(args[1]);
 197         for (int i = 2; i < args.length; i += 4) {
 198             try (Writer actual = Files.newBufferedWriter(Paths.get(args[i + 2]));
 199                  Writer expected = Files.newBufferedWriter(Paths.get(args[i + 3]))) {
 200                 run(actual, args[i + 1]);
 201                 realClasses(args[i], ignoreList, expected, args[i + 1]);
 202             }
 203         }
 204     }
 205 
 206     Predicate<String> constructAcceptIgnoreList(String fromFiles) throws IOException {
 207         StringBuilder acceptPattern = new StringBuilder();
 208         StringBuilder rejectPattern = new StringBuilder();
 209         for (String file : fromFiles.split(File.pathSeparator)) {
 210             try (Stream<String> lines = Files.lines(Paths.get(file))) {
 211                 lines.forEach(line -> {
 212                     if (line.isEmpty())
 213                         return;
 214                     StringBuilder targetPattern;
 215                     switch (line.charAt(0)) {
 216                         case '+':
 217                             targetPattern = acceptPattern;
 218                             break;
 219                         case '-':
 220                             targetPattern = rejectPattern;
 221                             break;
 222                         default:
 223                             return ;
 224                     }
 225                     line = line.substring(1);
 226                     if (line.endsWith("/")) {
 227                         line += "[^/]*";
 228                     } else {
 229                         line += "|" + line + "$[^/]*";
 230                     }
 231                     line = line.replace("/", ".");
 232                     if (targetPattern.length() != 0)
 233                         targetPattern.append("|");
 234                     targetPattern.append(line);
 235                 });
 236             }
 237         }
 238         Pattern accept = Pattern.compile(acceptPattern.toString());
 239         Pattern reject = Pattern.compile(rejectPattern.toString());
 240 
 241         return clazzName -> accept.matcher(clazzName).matches() && !reject.matcher(clazzName).matches();
 242     }
 243 
 244     private static String toHex(byte[] bytes) {
 245         StringBuilder hex = new StringBuilder();
 246         String delim = "";
 247 
 248         for (byte b : bytes) {
 249             hex.append(delim);
 250             hex.append(String.format("(byte) 0x%02X", b));
 251             delim = ", ";
 252         }
 253 
 254         return hex.toString();
 255     }
 256 
 257     void run(Writer output, String version) throws Exception {
 258         List<String> options = Arrays.asList("--release", version, "-classpath", "");
 259         List<ToolBox.JavaSource> files = Arrays.asList(new ToolBox.JavaSource("Test", ""));
 260         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 261         JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, options, null, files);
 262 
 263         task.analyze();
 264 
 265         JavaFileManager fm = task.getContext().get(JavaFileManager.class);
 266 
 267         for (String pack : packages(fm)) {
 268             PackageElement packEl = task.getElements().getPackageElement(pack);
 269             if (packEl == null) {
 270                 throw new AssertionError("Cannot find package: " + pack);
 271             }
 272             new ExhaustiveElementScanner(task, output, p -> true).visit(packEl);
 273         }
 274     }
 275 
 276     void realClasses(String location, Predicate<String> acceptor, Writer output, String version) throws Exception {
 277         Path classes = Paths.get(location);
 278         Map<String, JavaFileObject> className2File = new HashMap<>();
 279         Map<JavaFileObject, String> file2ClassName = new HashMap<>();
 280 
 281         try (BufferedReader descIn = Files.newBufferedReader(classes)) {
 282             String classFileData;
 283 
 284             while ((classFileData = descIn.readLine()) != null) {
 285                 ByteArrayOutputStream data = new ByteArrayOutputStream();
 286                 for (int i = 0; i < classFileData.length(); i += 2) {
 287                     data.write(Integer.parseInt(classFileData.substring(i, i + 2), 16));
 288                 }
 289                 JavaFileObject file = new ByteArrayJavaFileObject(data.toByteArray());
 290                 try (InputStream in = new ByteArrayInputStream(data.toByteArray())) {
 291                     String name = ClassFile.read(in).getName().replace("/", ".");
 292                     className2File.put(name, file);
 293                     file2ClassName.put(file, name);
 294                 } catch (IOException | ConstantPoolException ex) {
 295                     throw new IllegalStateException(ex);
 296                 }
 297             }
 298         }
 299 
 300         try (JavaFileManager fm = new TestFileManager(className2File, file2ClassName)) {
 301             JavacTaskImpl task = (JavacTaskImpl) ToolProvider.getSystemJavaCompiler().getTask(null, fm, null, Arrays.asList("-source", version), null, Arrays.asList(new ToolBox.JavaSource("Test", "")));
 302             task.parse();
 303 
 304             PACK: for (String pack : packages(fm)) {
 305                 PackageElement packEl = task.getElements().getPackageElement(pack);
 306                 assert packEl != null;
 307                 new ExhaustiveElementScanner(task, output, acceptor).visit(packEl);
 308             }
 309         }
 310     }
 311 
 312     Set<String> packages(JavaFileManager fm) throws IOException {
 313         Set<String> packages = new TreeSet<>();
 314         EnumSet<Kind> kinds = EnumSet.of(JavaFileObject.Kind.CLASS, JavaFileObject.Kind.OTHER);
 315 
 316         for (JavaFileObject file : fm.list(StandardLocation.PLATFORM_CLASS_PATH, "", kinds, true)) {
 317             String binary = fm.inferBinaryName(StandardLocation.PLATFORM_CLASS_PATH, file);
 318             packages.add(binary.substring(0, binary.lastIndexOf('.')));
 319         }
 320 
 321         return packages;
 322     }
 323 
 324     final class ExhaustiveElementScanner implements ElementVisitor<Void, Void> {
 325 
 326         final JavacTask task;
 327         final Writer out;
 328         final Predicate<String> acceptType;
 329 
 330         public ExhaustiveElementScanner(JavacTask task, Writer out, Predicate<String> acceptType) {
 331             this.task = task;
 332             this.out = out;
 333             this.acceptType = acceptType;
 334         }
 335 
 336         @Override
 337         public Void visit(Element e, Void p) {
 338             return e.accept(this, p);
 339         }
 340 
 341         @Override
 342         public Void visit(Element e) {
 343             return e.accept(this, null);
 344         }
 345 
 346         private void write(TypeMirror type) throws IOException {
 347             try {
 348                 out.write(type.toString()
 349                               .replace("java.lang.invoke.MethodHandle$PolymorphicSignature", "java.lang.invoke.MethodHandle.PolymorphicSignature")
 350                               .replace("javax.swing.JRootPane$DefaultAction", "javax.swing.JRootPane.DefaultAction")
 351                               .replace("javax.swing.plaf.metal.MetalFileChooserUI$DirectoryComboBoxRenderer", "javax.swing.plaf.metal.MetalFileChooserUI.DirectoryComboBoxRenderer")
 352                          );
 353             } catch (CompletionFailure cf) {
 354                 out.write("cf");
 355             }
 356         }
 357 
 358         private void writeTypes(Iterable<? extends TypeMirror> types) throws IOException {
 359             String sep = "";
 360 
 361             for (TypeMirror type : types) {
 362                 out.write(sep);
 363                 write(type);
 364                 sep = ", ";
 365             }
 366         }
 367 
 368         private void writeAnnotations(Iterable<? extends AnnotationMirror> annotations) throws IOException {
 369             for (AnnotationMirror ann : annotations) {
 370                 out.write("@");
 371                 write(ann.getAnnotationType());
 372                 if (!ann.getElementValues().isEmpty()) {
 373                     out.write("(");
 374                     Map<ExecutableElement, AnnotationValue> valuesMap = new TreeMap<>((a1, a2) -> a1.getSimpleName().toString().compareTo(a2.getSimpleName().toString()));
 375                     valuesMap.putAll(ann.getElementValues());
 376                     for (Entry<? extends ExecutableElement, ? extends AnnotationValue> ev : valuesMap.entrySet()) {
 377                         out.write(ev.getKey().getSimpleName().toString());
 378                         out.write(" = ");
 379                         out.write(ev.getValue().toString());
 380                     }
 381                     out.write(")");
 382                 }
 383             }
 384         }
 385 
 386         void analyzeElement(Element e) {
 387             try {
 388                 write(e.asType());
 389                 writeAnnotations(e.getAnnotationMirrors());
 390                 out.write(e.getKind().toString());
 391                 out.write(e.getModifiers().toString());
 392                 out.write(e.getSimpleName().toString());
 393             } catch (IOException ex) {
 394                 ex.printStackTrace();
 395             }
 396         }
 397 
 398         boolean acceptAccess(Element e) {
 399             return e.getModifiers().contains(Modifier.PUBLIC) || e.getModifiers().contains(Modifier.PROTECTED);
 400         }
 401 
 402         @Override
 403         public Void visitExecutable(ExecutableElement e, Void p) {
 404             if (!acceptAccess(e))
 405                 return null;
 406             try {
 407                 analyzeElement(e);
 408                 out.write(String.valueOf(e.getDefaultValue()));
 409                 for (VariableElement param : e.getParameters()) {
 410                     visit(param, p);
 411                 }
 412                 out.write(String.valueOf(typeMirrorTranslate(e.getReceiverType())));
 413                 write(e.getReturnType());
 414                 out.write(e.getSimpleName().toString());
 415                 writeTypes(e.getThrownTypes());
 416                 for (TypeParameterElement param : e.getTypeParameters()) {
 417                     visit(param, p);
 418                 }
 419                 out.write(String.valueOf(e.isDefault()));
 420                 out.write(String.valueOf(e.isVarArgs()));
 421                 out.write("\n");
 422             } catch (IOException ex) {
 423                 ex.printStackTrace();
 424             }
 425             return null;
 426         }
 427 
 428         /**
 429          * Original implementation of getReceiverType returned null
 430          * for many cases where TypeKind.NONE was specified; translate
 431          * back to null to compare against old hashes.
 432          */
 433         private TypeMirror typeMirrorTranslate(TypeMirror type) {
 434             if (type.getKind() == javax.lang.model.type.TypeKind.NONE)
 435                 return null;
 436             else
 437                 return type;
 438         }
 439 
 440         @Override
 441         public Void visitPackage(PackageElement e, Void p) {
 442             List<Element> types = new ArrayList<>(e.getEnclosedElements());
 443             Collections.sort(types, (e1, e2) -> e1.getSimpleName().toString().compareTo(e2.getSimpleName().toString()));
 444             for (Element encl : types) {
 445                 visit(encl, p);
 446             }
 447             return null;
 448         }
 449 
 450         @Override
 451         public Void visitType(TypeElement e, Void p) {
 452             if (!acceptAccess(e))
 453                 return null;
 454             writeType(e);
 455             return null;
 456         }
 457 
 458         void writeType(TypeElement e) {
 459             if (!acceptType.test(task.getElements().getBinaryName(e).toString()))
 460                 return ;
 461             try {
 462                 analyzeElement(e);
 463                 writeTypes(e.getInterfaces());
 464                 out.write(e.getNestingKind().toString());
 465                 out.write(e.getQualifiedName().toString());
 466                 write(e.getSuperclass());
 467                 for (TypeParameterElement param : e.getTypeParameters()) {
 468                     visit(param, null);
 469                 }
 470                 List<Element> defs = new ArrayList<>(e.getEnclosedElements()); //XXX: forcing ordering for members - not completely correct!
 471                 Collections.sort(defs, (e1, e2) -> e1.toString().compareTo(e2.toString()));
 472                 for (Element def : defs) {
 473                     visit(def, null);
 474                 }
 475                 out.write("\n");
 476             } catch (IOException ex) {
 477                 ex.printStackTrace();
 478             }
 479         }
 480 
 481         @Override
 482         public Void visitVariable(VariableElement e, Void p) {
 483             if (!acceptAccess(e))
 484                 return null;
 485             try {
 486                 analyzeElement(e);
 487                 out.write(String.valueOf(e.getConstantValue()));
 488                 out.write("\n");
 489             } catch (IOException ex) {
 490                 ex.printStackTrace();
 491             }
 492             return null;
 493         }
 494 
 495         @Override
 496         public Void visitTypeParameter(TypeParameterElement e, Void p) {
 497             try {
 498                 analyzeElement(e);
 499                 out.write(e.getBounds().toString());
 500                 out.write("\n");
 501             } catch (IOException ex) {
 502                 ex.printStackTrace();
 503             }
 504             return null;
 505         }
 506 
 507         @Override
 508         public Void visitModule(ModuleElement e, Void p) {
 509             throw new IllegalStateException("Not supported yet.");
 510         }
 511 
 512         @Override
 513         public Void visitUnknown(Element e, Void p) {
 514             throw new IllegalStateException("Should not get here.");
 515         }
 516 
 517     }
 518 
 519     final class TestFileManager implements JavaFileManager {
 520 
 521         final Map<String, JavaFileObject> className2File;
 522         final Map<JavaFileObject, String> file2ClassName;
 523 
 524         public TestFileManager(Map<String, JavaFileObject> className2File, Map<JavaFileObject, String> file2ClassName) {
 525             this.className2File = className2File;
 526             this.file2ClassName = file2ClassName;
 527         }
 528 
 529         @Override
 530         public ClassLoader getClassLoader(Location location) {
 531             return new URLClassLoader(new URL[0]);
 532         }
 533 
 534         @Override
 535         public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
 536             if (location != StandardLocation.PLATFORM_CLASS_PATH || !kinds.contains(Kind.CLASS))
 537                 return Collections.emptyList();
 538 
 539             if (!packageName.isEmpty())
 540                 packageName += ".";
 541 
 542             List<JavaFileObject> result = new ArrayList<>();
 543 
 544             for (Entry<String, JavaFileObject> e : className2File.entrySet()) {
 545                 String currentPackage = e.getKey().substring(0, e.getKey().lastIndexOf(".") + 1);
 546                 if (recurse ? currentPackage.startsWith(packageName) : packageName.equals(currentPackage))
 547                     result.add(e.getValue());
 548             }
 549 
 550             return result;
 551         }
 552 
 553         @Override
 554         public String inferBinaryName(Location location, JavaFileObject file) {
 555             return file2ClassName.get(file);
 556         }
 557 
 558         @Override
 559         public boolean isSameFile(FileObject a, FileObject b) {
 560             return a == b;
 561         }
 562 
 563         @Override
 564         public boolean handleOption(String current, Iterator<String> remaining) {
 565             return false;
 566         }
 567 
 568         @Override
 569         public boolean hasLocation(Location location) {
 570             return location == StandardLocation.PLATFORM_CLASS_PATH;
 571         }
 572 
 573         @Override
 574         public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
 575             if (location != StandardLocation.PLATFORM_CLASS_PATH || kind != Kind.CLASS)
 576                 return null;
 577 
 578             return className2File.get(className);
 579         }
 580 
 581         @Override
 582         public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
 583             throw new UnsupportedOperationException("");
 584         }
 585 
 586         @Override
 587         public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
 588             return null;
 589         }
 590 
 591         @Override
 592         public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
 593             throw new UnsupportedOperationException("");
 594         }
 595 
 596         @Override
 597         public void flush() throws IOException {
 598         }
 599 
 600         @Override
 601         public void close() throws IOException {
 602         }
 603 
 604         @Override
 605         public int isSupportedOption(String option) {
 606             return -1;
 607         }
 608 
 609     }
 610 
 611     static class ByteArrayJavaFileObject implements JavaFileObject {
 612 
 613         private final byte[] data;
 614 
 615         public ByteArrayJavaFileObject(byte[] data) {
 616             this.data = data;
 617         }
 618 
 619         @Override
 620         public Kind getKind() {
 621             return Kind.CLASS;
 622         }
 623 
 624         @Override
 625         public boolean isNameCompatible(String simpleName, Kind kind) {
 626             return true;
 627         }
 628 
 629         @Override
 630         public NestingKind getNestingKind() {
 631             return null;
 632         }
 633 
 634         @Override
 635         public Modifier getAccessLevel() {
 636             return null;
 637         }
 638 
 639         @Override
 640         public URI toUri() {
 641             return null;
 642         }
 643 
 644         @Override
 645         public String getName() {
 646             return null;
 647         }
 648 
 649         @Override
 650         public InputStream openInputStream() throws IOException {
 651             return new ByteArrayInputStream(data);
 652         }
 653 
 654         @Override
 655         public OutputStream openOutputStream() throws IOException {
 656             throw new UnsupportedOperationException();
 657         }
 658 
 659         @Override
 660         public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
 661             throw new UnsupportedOperationException();
 662         }
 663 
 664         @Override
 665         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
 666             throw new UnsupportedOperationException();
 667         }
 668 
 669         @Override
 670         public Writer openWriter() throws IOException {
 671             throw new UnsupportedOperationException();
 672         }
 673 
 674         @Override
 675         public long getLastModified() {
 676             return 0;
 677         }
 678 
 679         @Override
 680         public boolean delete() {
 681             throw new UnsupportedOperationException();
 682         }
 683     }
 684 
 685 }