class ExtractJarsTransform extends Transform {

    @Override
    public void transform(@NonNull TransformInvocation transformInvocation)
            throws IOException, TransformException, InterruptedException {
        TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
        boolean isIncremental = transformInvocation.isIncremental();
        checkNotNull(outputProvider, "Missing output object for transform " + getName());

        // as_input transform and no referenced scopes, all the inputs will in InputOutputStreams.
        final boolean extractCode = contentTypes.contains(DefaultContentType.CLASSES);

        if (!isIncremental) {
            outputProvider.deleteAll();
        }

        try {
            WaitableExecutor executor = WaitableExecutor.useGlobalSharedThreadPool();

            for (TransformInput input : transformInvocation.getInputs()) {
                for (DirectoryInput dirInput : input.getDirectoryInputs()) {
                    File dirOutput =
                            outputProvider.getContentLocation(
                                    dirInput.getFile().toString(),
                                    dirInput.getContentTypes(),
                                    dirInput.getScopes(),
                                    Format.DIRECTORY);
                    // since we do a full copy, we have to delete the output first.
                    // FIXME make this incremental!
                    if (dirOutput.isDirectory()) {
                        FileUtils.deleteDirectoryContents(dirOutput);
                    }
                    if (dirInput.getFile().exists()) {
                        org.apache.commons.io.FileUtils.copyDirectory(
                                dirInput.getFile(), dirOutput);
                    }
                }

                for (JarInput jarInput : input.getJarInputs()) {
                    final File jarFile = jarInput.getFile();

                    // create an output folder for this jar, keeping its type and scopes.
                    final File outJarFolder =
                            outputProvider.getContentLocation(
                                    jarInput.getFile().toString(),
                                    jarInput.getContentTypes(),
                                    jarInput.getScopes(),
                                    Format.DIRECTORY);
                    FileUtils.mkdirs(outJarFolder);

                    if (!isIncremental) {
                        executor.execute(() -> {
                            extractJar(outJarFolder, jarFile, extractCode);
                            return null;
                        });
                    } else {
                        switch (jarInput.getStatus()) {
                            case CHANGED:
                                executor.execute(() -> {
                                    FileUtils.cleanOutputDir(outJarFolder);
                                    extractJar(outJarFolder, jarFile, extractCode);
                                    return null;
                                });
                                break;
                            case ADDED:
                                executor.execute(() -> {
                                    extractJar(outJarFolder, jarFile, extractCode);
                                    return null;
                                });
                                break;
                            case REMOVED:
                                executor.execute(
                                        () -> {
                                            FileUtils.cleanOutputDir(outJarFolder);
                                            return null;
                                        });
                                break;
                            case NOTCHANGED:
                                break;
                        }
                    }
                }
            }

            executor.waitForTasksWithQuickFail(true);

        } catch (InterruptedException e) {
            throw e;
        } catch (Exception e) {
            throw new TransformException(e);
        }
    }

    public  ExtractJarsTransform(
            @NonNull Set<ContentType> contentTypes,
            @NonNull Set<Scope> scopes);

}