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