Example #1
0
  /**
   * Save a compiled class and associated classes to a jar file.
   *
   * <p>With a packageName = "" and recursive = false, it will save clazz and any classes compiled
   * from the same source (I think); this is probably what you want.
   *
   * @param packageName package name prefix to search for classes, or "" for all
   * @param clazz a class that has been previously compiled by this bridge
   * @param mainClazz a class that will be installed as the "Main-Class" of a runnable jar
   * @param outStream output stream
   * @param recursive whether to retrieve classes from rest of the the JavaFileManager hierarchy
   * @throws FileNotFoundException
   * @throws IOException
   */
  public void saveToJar(
      String packageName,
      Class<?> clazz,
      Class<?> mainClazz,
      OutputStream outStream,
      boolean recursive)
      throws IOException {
    JavaFileManager manager = fileManagerCache.get(clazz);
    List<JavaFileObject> list = new ArrayList<JavaFileObject>();

    for (JavaFileObject obj :
        manager.list(
            StandardLocation.CLASS_PATH,
            packageName,
            Collections.singleton(JavaFileObject.Kind.CLASS),
            false)) list.add(obj);

    if (list.iterator().hasNext()) {
      Manifest manifest = new Manifest();
      manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");

      if (mainClazz != null) {
        manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, mainClazz.getName());
      }
      manifest
          .getMainAttributes()
          .put(new Attributes.Name("X-Rascal-Saved-Class"), clazz.getName());
      JarOutputStream target = new JarOutputStream(outStream, manifest);
      JarEntry entry = new JarEntry("META-INF/");
      target.putNextEntry(entry);
      Collection<String> dirs = new ArrayList<String>();

      for (JavaFileObject o : list) {
        String path = o.toUri().getPath().replace(".", "/");
        makeJarDirs(target, dirs, path);
        entry = new JarEntry(path + ".class");
        entry.setTime(o.getLastModified());
        target.putNextEntry(entry);

        try (InputStream stream = o.openInputStream()) {
          byte[] buffer = new byte[8192];
          int c = stream.read(buffer);
          while (c > -1) {
            target.write(buffer, 0, c);
            c = stream.read(buffer);
          }
        }
        target.closeEntry();
      }

      if (mainClazz != null) {
        String name = mainClazz.getName();
        String path = name.replace(".", "/") + ".class";

        String dir = path.substring(0, path.lastIndexOf('/'));
        StringBuilder dirTmp = new StringBuilder(dir.length());
        for (String d : dir.split("/")) {
          dirTmp.append(d);
          dirTmp.append("/");
          String tmp = dirTmp.toString();
          if (!dirs.contains(tmp)) {
            dirs.add(tmp);
            entry = new JarEntry(tmp);
            target.putNextEntry(entry);
          }
        }
        entry = new JarEntry(path);
        target.putNextEntry(entry);

        try (InputStream stream = mainClazz.getClassLoader().getResourceAsStream(path)) {
          byte[] buffer = new byte[8192];
          int c = stream.read(buffer);
          while (c > -1) {
            target.write(buffer, 0, c);
            c = stream.read(buffer);
          }
        }
        target.closeEntry();
      }

      target.close();
    }
  }
    @Override
    public Iterable<JavaFileObject> list(
        Location location, String packageName, Set<Kind> kinds, boolean recurse)
        throws IOException {
      if (location == StandardLocation.PLATFORM_CLASS_PATH) {
        return standardJavaFileManager.list(location, packageName, kinds, recurse);
      }
      List<JavaFileObject> ret = new ArrayList<>();
      if (kinds.contains(Kind.CLASS) && location.equals(StandardLocation.CLASS_PATH)) {
        if (classLoader instanceof ModuleClassLoader) {
          ModuleClassLoader mcl = (ModuleClassLoader) classLoader;
          final String packageWithSlashes = packageName.replace(".", "/");
          try {
            Iterator<Resource> resources =
                mcl.getModule()
                    .iterateResources(
                        new PathFilter() {
                          @Override
                          public boolean accept(String path) {
                            if (recurse) {
                              return path.startsWith(packageWithSlashes);
                            } else {
                              return path.equals(packageWithSlashes);
                            }
                          }
                        });
            while (resources.hasNext()) {
              Resource res = resources.next();
              if (!res.getName().endsWith(".class")) {
                continue;
              }
              String binaryName =
                  res.getName().replace("/", ".").substring(0, res.getName().length() - 6);
              try {
                ret.add(
                    new ZipJavaFileObject(
                        org.fakereplace.util.FileReader.readFileBytes(res.openStream()),
                        binaryName,
                        res.getURL().toURI()));
              } catch (URISyntaxException e) {
                e.printStackTrace();
              }
            }
          } catch (ModuleLoadException e) {
            e.printStackTrace();
          }
        } else {
          URL res = classLoader.getResource(packageName.replace(".", "/"));
          if (res != null) {
            if (res.getProtocol().equals("file")) {
              Path dirPath = Paths.get(res.getFile());
              listDir(packageName, dirPath, recurse, ret);
            } else if (res.getProtocol().equals("jar")) {
              JarURLConnection connection = (JarURLConnection) res.openConnection();
              Enumeration<JarEntry> entryEnum = connection.getJarFile().entries();
              while (entryEnum.hasMoreElements()) {
                JarEntry entry = entryEnum.nextElement();
                String name = entry.getName();
                if (name.endsWith(".class") && !entry.isDirectory()) {
                  if (name.startsWith(packageName.replace(".", "/"))) {
                    String rem = name.substring(packageName.length());
                    if (rem.startsWith("/")) {
                      rem = rem.substring(1);
                    }
                    if (!recurse) {
                      if (rem.contains("/")) {
                        continue;
                      }
                    }
                    String binaryName =
                        entry
                            .getName()
                            .replace("/", ".")
                            .substring(0, entry.getName().length() - 6);
                    try {
                      URI uri = new URI(res.toExternalForm() + "/" + rem);
                      ret.add(
                          new ZipJavaFileObject(
                              org.fakereplace.util.FileReader.readFileBytes(
                                  uri.toURL().openStream()),
                              binaryName,
                              uri));
                    } catch (Exception e) {
                      e.printStackTrace();
                    }
                  }
                }
              }
            } else {
              System.err.println(
                  "Could not find package "
                      + packageName
                      + " in "
                      + classLoader
                      + " unknown protocol "
                      + res.getProtocol());
            }
          } else {
            return standardJavaFileManager.list(location, packageName, kinds, recurse);
          }
        }
      } else {
        return standardJavaFileManager.list(location, packageName, kinds, recurse);
      }

      return ret;
    }