ArchiveBuilder.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 io.earcam.instrumental.archive.ArchiveResourceSource.ResourceSourceLifecycle.FINAL;
import static io.earcam.instrumental.archive.ArchiveResourceSource.ResourceSourceLifecycle.INITIAL;
import static io.earcam.instrumental.archive.ArchiveResourceSource.ResourceSourceLifecycle.PRE_MANIFEST;
import static java.util.stream.Collectors.toList;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import javax.annotation.WillClose;
import io.earcam.instrumental.archive.ArchiveResourceSource.ResourceSourceLifecycle;
class ArchiveBuilder implements ArchiveConfiguration, ArchiveRegistrar {
private final BasicArchiveResourceSource basicSource = new BasicArchiveResourceSource();
private final List<ArchiveResourceSource> sources = new ArrayList<>();
private final List<ArchiveResourceListener> listeners = new ArrayList<>();
private final List<ManifestProcessor> manifestProcessors = new ArrayList<>();
private final List<ArchiveResourceFilter> filters = new ArrayList<>();
ArchiveBuilder()
{
registerResourceSource(basicSource);
}
@Override
public ArchiveRegistrar registerResourceSource(ArchiveResourceSource source)
{
sources.add(source);
return this;
}
@Override
public ArchiveRegistrar registerResourceListener(ArchiveResourceListener listener)
{
listeners.add(listener);
return this;
}
@Override
public ArchiveRegistrar registerResourceFilter(ArchiveResourceFilter filter)
{
filters.add(filter);
return this;
}
@Override
public ArchiveRegistrar registerManifestProcessor(ManifestProcessor processor)
{
manifestProcessors.add(processor);
return this;
}
@Override
public ArchiveConfiguration configured(ArchiveConfigurationPlugin plugin)
{
plugin.attach(this);
return this;
}
@Override
public ArchiveConstruction filteredBy(ArchiveResourceFilter filter)
{
registerResourceFilter(filter);
return this;
}
@Override
public ArchiveConstruction sourcing(ArchiveResourceSource source)
{
registerResourceSource(source);
return this;
}
@Override
public ArchiveConstruction with(Class<?> type)
{
basicSource.with(type);
return this;
}
@Override
public ArchiveConstruction with(String name, InputStream resource)
{
basicSource.with(name, resource);
return this;
}
@Override
public ArchiveConstruction with(String name, byte[] contents)
{
basicSource.with(name, contents);
return this;
}
@Override
public Archive toObjectModel()
{
// @ this point no resources will have been filtered/listened
// so we need to call all the resource-sources
List<ArchiveResource> list = processRound(INITIAL);
updateListeners(list);
List<ArchiveResource> plist = processRound(PRE_MANIFEST);
updateListeners(plist);
list.addAll(plist);
// Then invoke the manifestProcessors
Manifest manifest = new Manifest();
for(ManifestProcessor processor : manifestProcessors) {
processor.process(manifest);
}
// Then final call for resources post-manifest
plist = processRound(FINAL);
updateListeners(plist);
list.addAll(plist);
return new DefaultArchive(optional(manifest), list);
}
private void updateListeners(List<ArchiveResource> list)
{
for(ArchiveResource resource : list) {
for(ArchiveResourceListener listener : listeners) {
listener.added(resource);
}
}
}
private List<ArchiveResource> processRound(ResourceSourceLifecycle stage)
{
Stream<ArchiveResource> current = Stream.empty();
for(ArchiveResourceSource source : sources) {
current = Stream.concat(current, source.drain(stage));
}
for(ArchiveResourceFilter filter : filters) {
current = current.map(filter::apply);
}
return current.filter(Objects::nonNull).collect(toList());
}
private static Optional<Manifest> optional(Manifest manifest)
{
return isEmpty(manifest) ? Optional.empty() : Optional.of(manifest);
}
private static boolean isEmpty(Manifest manifest)
{
return manifest.getMainAttributes().isEmpty()
&& manifest.getEntries().isEmpty();
}
@Override
public <R> R to(Function<Archive, R> transformer)
{
return transformer.apply(toObjectModel());
}
@Override
public void to(@WillClose OutputStream os)
{
toObjectModel().to(os);
}
@Override
public Path explodeTo(Path directory, boolean merge)
{
return toObjectModel().explodeTo(directory, merge);
}
}