@Override
  public void processResource(String resource, InputStream is, List<Relocator> relocators)
      throws IOException {
    ServiceStream out = serviceEntries.get(resource);
    String key = resource.substring(SERVICES_PATH.length() + 1);
    boolean relocatable = false;

    for (Relocator relocator : relocators) {
      if (relocator.canRelocateClass(key)) {
        relocatable = true;
        break;
      }
    }

    if (relocatable && out == null) {
      out = new ServiceStream();
      serviceEntries.put(resource, out);
    }

    if (out != null) {
      out.append(is);
      is.close();
    }

    if (this.relocators == null) {
      this.relocators = relocators;
    }
  }
  @Override
  public void modifyOutputStream(JarOutputStream jos) throws IOException {
    for (Map.Entry<String, ServiceStream> entry : serviceEntries.entrySet()) {
      String key = entry.getKey();
      ServiceStream data = entry.getValue();

      if (relocators != null) {
        key = key.substring(SERVICES_PATH.length() + 1);
        for (Relocator relocator : relocators) {
          if (relocator.canRelocateClass(key)) {
            key = relocator.relocateClass(key);
            break;
          }
        }
        key = SERVICES_PATH + '/' + key;
      }

      jos.putNextEntry(new JarEntry(key));

      PrintWriter writer = new PrintWriter(jos);
      InputStreamReader streamReader = new InputStreamReader(data.toInputStream());
      try (BufferedReader reader = new BufferedReader(streamReader)) {
        String className;

        while ((className = reader.readLine()) != null) {
          if (relocators != null) {
            for (Relocator relocator : relocators) {
              if (relocator.canRelocateClass(className)) {
                className = relocator.applyToSourceContent(className);
                break;
              }
            }
          }

          writer.println(className);
          writer.flush();
        }
      }

      data.reset();
    }
  }