ArchiveConstruction.java
/*-
* #%L
* io.earcam.instrumental.archive
* %%
* Copyright (C) 2018 earcam
* %%
* SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
*
* You <b>must</b> choose to accept, in full - any individual or combination of
* the following licenses:
* <ul>
* <li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
* <li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
* <li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
* <li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
* </ul>
* #L%
*/
package io.earcam.instrumental.archive;
import static java.util.stream.Collectors.toList;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import io.earcam.instrumental.fluent.Fluent;
import io.earcam.unexceptional.CheckedBiFunction;
import io.earcam.unexceptional.Exceptional;
import io.earcam.utilitarian.io.ExplodedJarInputStream;
import io.earcam.utilitarian.io.ExplodedJarInputStream.ExplodedJarEntry;
import io.earcam.utilitarian.io.IoStreams;
/**
* <p>
* ArchiveConstruction interface.
* </p>
*
*/
public interface ArchiveConstruction extends ArchiveTransform {
public static final class ContentFilters {
public static final Predicate<String> NO_FILTER = x -> true;
private ContentFilters()
{}
}
/**
* <p>
* filteredBy.
* </p>
*
* @param filter a {@link io.earcam.instrumental.archive.ArchiveResourceFilter} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public abstract ArchiveConstruction filteredBy(ArchiveResourceFilter filter);
/**
* <p>
* sourcing.
* </p>
*
* @param source a {@link io.earcam.instrumental.archive.ArchiveResourceSource} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public abstract ArchiveConstruction sourcing(ArchiveResourceSource source);
/**
* <p>
* with.
* </p>
*
* @param types a {@link java.lang.Class} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public default ArchiveConstruction with(Class<?>... types)
{
Arrays.stream(types).forEach(this::with);
return this;
}
/**
* <p>
* with.
* </p>
*
* @param type a {@link java.lang.Class} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public abstract ArchiveConstruction with(Class<?> type);
/**
* <p>
* with.
* </p>
*
* @param name a {@link java.lang.String} object.
* @param resource a {@link java.nio.file.Path} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public default ArchiveConstruction with(String name, Path resource)
{
return with(name, resource.toFile());
}
/**
* <p>
* with.
* </p>
*
* @param name a {@link java.lang.String} object.
* @param resource a {@link java.io.File} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public default ArchiveConstruction with(String name, File resource)
{
InputStream inputStream = Exceptional.apply(FileInputStream::new, resource);
return with(name, inputStream);
}
/**
* <p>
* with.
* </p>
*
* @param name a {@link java.lang.String} object.
* @param resource a {@link java.io.InputStream} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public abstract ArchiveConstruction with(String name, InputStream resource);
/**
* <p>
* with.
* </p>
*
* @param name a {@link java.lang.String} object.
* @param contents an array of {@link byte} objects.
* @return a {@link io.earcam.instrumental.archive.ArchiveConstruction} object.
*/
public abstract ArchiveConstruction with(String name, byte[] contents);
/**
* <p>
* Include an Archive's resources in the construction of another.
* </p>
* <p>
* Note: this does not copy the manifest, see {@link AbstractAsJarBuilder#mergingManifest(java.util.jar.Manifest)}
* </p>
*
* @param archive the archive with content
* @return an {@link ArchiveResourceSource} that will be drained on first call.
*
* @see #contentFrom(Archive, ArchiveResourceFilter)
* @see #sourcing(ArchiveResourceSource)
*/
public static ArchiveResourceSource contentFrom(Archive archive)
{
ArchiveResourceFilter noop = UnaryOperator.<ArchiveResource> identity()::apply;
return contentFrom(archive, noop);
}
/**
* <p>
* Include an Archive's resources in the construction of another, using the supplied {@code filter}
* to optionally modify or exclude content.
* </p>
* <p>
* Note: this does not copy the manifest, see {@link AbstractAsJarBuilder#mergingManifest(java.util.jar.Manifest)}
* </p>
*
* @param archive the archive with content
* @param filter the filter to apply to content from the archive
* @return an {@link ArchiveResourceSource} that will be drained on first call.
*
* @see #contentFrom(Archive)
* @see #sourcing(ArchiveResourceSource)
*/
public static ArchiveResourceSource contentFrom(Archive archive, ArchiveResourceFilter filter)
{
List<ArchiveResource> resources = archive.contents().stream()
.map(filter)
.filter(Objects::nonNull)
.collect(toList());
return ArchiveResourceSource.wrap(resources);
}
/**
* <p>
* contentFrom.
* </p>
*
* @param path a {@link java.nio.file.Path} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveResourceSource} object.
*
* @see #sourcing(ArchiveResourceSource)
*/
@Fluent
public static ArchiveResourceSource contentFrom(Path path)
{
return contentFrom(path, ContentFilters.NO_FILTER);
}
/**
* <p>
* contentFrom.
* </p>
*
* @param path a {@link java.io.File} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveResourceSource} object.
*
* @see #sourcing(ArchiveResourceSource)
*/
@Fluent
public static ArchiveResourceSource contentFrom(File path)
{
return contentFrom(path, ContentFilters.NO_FILTER);
}
/**
* <p>
* contentFrom.
* </p>
*
* @param path a {@link java.io.File} object.
* @param filter a {@link java.util.function.Predicate} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveResourceSource} object.
*
* @see #sourcing(ArchiveResourceSource)
*/
@Fluent
public static ArchiveResourceSource contentFrom(File path, Predicate<String> filter)
{
return contentFrom(path.toPath(), filter);
}
/**
* <p>
* contentFrom.
* </p>
*
* @param path a {@link java.nio.file.Path} object.
* @param filter a {@link java.util.function.Predicate} object.
* @return a {@link io.earcam.instrumental.archive.ArchiveResourceSource} object.
*
* @see #sourcing(ArchiveResourceSource)0
*/
@Fluent
public static ArchiveResourceSource contentFrom(Path path, Predicate<String> filter)
{
JarInputStream inputStream = Exceptional.apply(ExplodedJarInputStream::jarInputStreamFrom, path);
return contentFrom(inputStream, filter);
}
/**
*
* @param inputStream
* @param filter
* @return
*
* @see #sourcing(ArchiveResourceSource)
*/
@Fluent
public static ArchiveResourceSource contentFrom(JarInputStream inputStream, Predicate<String> filter)
{
CheckedBiFunction<JarInputStream, Predicate<String>, ArchiveResourceSource, IOException> extract = new CheckedBiFunction<JarInputStream, Predicate<String>, ArchiveResourceSource, IOException>() {
@Override
public ArchiveResourceSource apply(JarInputStream t, Predicate<String> u) throws IOException
{
BasicArchiveResourceSource source = new BasicArchiveResourceSource();
JarEntry entry;
try(JarInputStream jin = inputStream) {
while((entry = jin.getNextJarEntry()) != null) {
if(!filter.test(entry.getName()) || entry.isDirectory()) {
/* noop */
} else if(entry instanceof ExplodedJarEntry) {
source.with(entry.getName(), new FileInputStream(((ExplodedJarEntry) entry).path().toFile()));
} else {
source.with(entry.getName(), IoStreams.readAllBytes(jin));
}
}
}
return source;
}
};
return Exceptional.apply(extract, inputStream, filter);
}
/**
* <p>
* toObjectModel.
* </p>
*
* @return a {@link io.earcam.instrumental.archive.Archive} object.
*/
public abstract Archive toObjectModel();
}