1 /*
   2  * Copyright (c) 2015, 2018, 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) 0x6B, (byte) 0xA2, (byte) 0xE9, (byte) 0x8E,
 132         (byte) 0xE1, (byte) 0x8E, (byte) 0x60, (byte) 0xBE,
 133         (byte) 0x54, (byte) 0xC4, (byte) 0x33, (byte) 0x3E,
 134         (byte) 0x0C, (byte) 0x2D, (byte) 0x3A, (byte) 0x7C
 135     };
 136     static final byte[] hash8 = new byte[] {
 137         (byte) 0x44, (byte) 0x77, (byte) 0x6E, (byte) 0x52,
 138         (byte) 0x2B, (byte) 0x16, (byte) 0xD3, (byte) 0x3C,
 139         (byte) 0x78, (byte) 0x75, (byte) 0xF5, (byte) 0x0A,
 140         (byte) 0x01, (byte) 0x24, (byte) 0xBD, (byte) 0x2A
 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(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         @Override
 429         public Void visitPackage(PackageElement e, Void p) {
 430             List<Element> types = new ArrayList<>(e.getEnclosedElements());
 431             Collections.sort(types, (e1, e2) -> e1.getSimpleName().toString().compareTo(e2.getSimpleName().toString()));
 432             for (Element encl : types) {
 433                 visit(encl, p);
 434             }
 435             return null;
 436         }
 437 
 438         @Override
 439         public Void visitType(TypeElement e, Void p) {
 440             if (!acceptAccess(e))
 441                 return null;
 442             writeType(e);
 443             return null;
 444         }
 445 
 446         void writeType(TypeElement e) {
 447             if (!acceptType.test(task.getElements().getBinaryName(e).toString()))
 448                 return ;
 449             try {
 450                 analyzeElement(e);
 451                 writeTypes(e.getInterfaces());
 452                 out.write(e.getNestingKind().toString());
 453                 out.write(e.getQualifiedName().toString());
 454                 write(e.getSuperclass());
 455                 for (TypeParameterElement param : e.getTypeParameters()) {
 456                     visit(param, null);
 457                 }
 458                 List<Element> defs = new ArrayList<>(e.getEnclosedElements()); //XXX: forcing ordering for members - not completely correct!
 459                 Collections.sort(defs, (e1, e2) -> e1.toString().compareTo(e2.toString()));
 460                 for (Element def : defs) {
 461                     visit(def, null);
 462                 }
 463                 out.write("\n");
 464             } catch (IOException ex) {
 465                 ex.printStackTrace();
 466             }
 467         }
 468 
 469         @Override
 470         public Void visitVariable(VariableElement e, Void p) {
 471             if (!acceptAccess(e))
 472                 return null;
 473             try {
 474                 analyzeElement(e);
 475                 out.write(String.valueOf(e.getConstantValue()));
 476                 out.write("\n");
 477             } catch (IOException ex) {
 478                 ex.printStackTrace();
 479             }
 480             return null;
 481         }
 482 
 483         @Override
 484         public Void visitTypeParameter(TypeParameterElement e, Void p) {
 485             try {
 486                 analyzeElement(e);
 487                 out.write(e.getBounds().toString());
 488                 out.write("\n");
 489             } catch (IOException ex) {
 490                 ex.printStackTrace();
 491             }
 492             return null;
 493         }
 494 
 495         @Override
 496         public Void visitModule(ModuleElement e, Void p) {
 497             throw new IllegalStateException("Not supported yet.");
 498         }
 499 
 500         @Override
 501         public Void visitUnknown(Element e, Void p) {
 502             throw new IllegalStateException("Should not get here.");
 503         }
 504 
 505     }
 506 
 507     final class TestFileManager implements JavaFileManager {
 508 
 509         final Map<String, JavaFileObject> className2File;
 510         final Map<JavaFileObject, String> file2ClassName;
 511 
 512         public TestFileManager(Map<String, JavaFileObject> className2File, Map<JavaFileObject, String> file2ClassName) {
 513             this.className2File = className2File;
 514             this.file2ClassName = file2ClassName;
 515         }
 516 
 517         @Override
 518         public ClassLoader getClassLoader(Location location) {
 519             return new URLClassLoader(new URL[0]);
 520         }
 521 
 522         @Override
 523         public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
 524             if (location != StandardLocation.PLATFORM_CLASS_PATH || !kinds.contains(Kind.CLASS))
 525                 return Collections.emptyList();
 526 
 527             if (!packageName.isEmpty())
 528                 packageName += ".";
 529 
 530             List<JavaFileObject> result = new ArrayList<>();
 531 
 532             for (Entry<String, JavaFileObject> e : className2File.entrySet()) {
 533                 String currentPackage = e.getKey().substring(0, e.getKey().lastIndexOf(".") + 1);
 534                 if (recurse ? currentPackage.startsWith(packageName) : packageName.equals(currentPackage))
 535                     result.add(e.getValue());
 536             }
 537 
 538             return result;
 539         }
 540 
 541         @Override
 542         public String inferBinaryName(Location location, JavaFileObject file) {
 543             return file2ClassName.get(file);
 544         }
 545 
 546         @Override
 547         public boolean isSameFile(FileObject a, FileObject b) {
 548             return a == b;
 549         }
 550 
 551         @Override
 552         public boolean handleOption(String current, Iterator<String> remaining) {
 553             return false;
 554         }
 555 
 556         @Override
 557         public boolean hasLocation(Location location) {
 558             return location == StandardLocation.PLATFORM_CLASS_PATH;
 559         }
 560 
 561         @Override
 562         public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
 563             if (location != StandardLocation.PLATFORM_CLASS_PATH || kind != Kind.CLASS)
 564                 return null;
 565 
 566             return className2File.get(className);
 567         }
 568 
 569         @Override
 570         public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
 571             throw new UnsupportedOperationException("");
 572         }
 573 
 574         @Override
 575         public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
 576             return null;
 577         }
 578 
 579         @Override
 580         public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
 581             throw new UnsupportedOperationException("");
 582         }
 583 
 584         @Override
 585         public void flush() throws IOException {
 586         }
 587 
 588         @Override
 589         public void close() throws IOException {
 590         }
 591 
 592         @Override
 593         public int isSupportedOption(String option) {
 594             return -1;
 595         }
 596 
 597     }
 598 
 599     static class ByteArrayJavaFileObject implements JavaFileObject {
 600 
 601         private final byte[] data;
 602 
 603         public ByteArrayJavaFileObject(byte[] data) {
 604             this.data = data;
 605         }
 606 
 607         @Override
 608         public Kind getKind() {
 609             return Kind.CLASS;
 610         }
 611 
 612         @Override
 613         public boolean isNameCompatible(String simpleName, Kind kind) {
 614             return true;
 615         }
 616 
 617         @Override
 618         public NestingKind getNestingKind() {
 619             return null;
 620         }
 621 
 622         @Override
 623         public Modifier getAccessLevel() {
 624             return null;
 625         }
 626 
 627         @Override
 628         public URI toUri() {
 629             return null;
 630         }
 631 
 632         @Override
 633         public String getName() {
 634             return null;
 635         }
 636 
 637         @Override
 638         public InputStream openInputStream() throws IOException {
 639             return new ByteArrayInputStream(data);
 640         }
 641 
 642         @Override
 643         public OutputStream openOutputStream() throws IOException {
 644             throw new UnsupportedOperationException();
 645         }
 646 
 647         @Override
 648         public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
 649             throw new UnsupportedOperationException();
 650         }
 651 
 652         @Override
 653         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
 654             throw new UnsupportedOperationException();
 655         }
 656 
 657         @Override
 658         public Writer openWriter() throws IOException {
 659             throw new UnsupportedOperationException();
 660         }
 661 
 662         @Override
 663         public long getLastModified() {
 664             return 0;
 665         }
 666 
 667         @Override
 668         public boolean delete() {
 669             throw new UnsupportedOperationException();
 670         }
 671     }
 672 
 673 }