Concatenator.java
/*
* @copyright defined in LICENSE.txt
*/
package ship.build;
import static hera.util.FilepathUtils.getCanonicalForm;
import static java.util.Optional.ofNullable;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.FileNotFoundException;
import java.nio.file.NoSuchFileException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.slf4j.Logger;
import ship.build.res.Source;
import ship.build.web.model.BuildDependency;
import ship.build.web.model.BuildDetails;
import ship.exception.BuildException;
@RequiredArgsConstructor
public class Concatenator {
protected final transient Logger logger = getLogger(getClass());
protected final CallStack callStack = new CallStack();
@Getter
protected final ResourceManager resourceManager;
@Getter
protected final Set<Resource> visitedResources;
public Concatenator(final ResourceManager resourceManager) {
this(resourceManager, new LinkedHashSet<>());
}
@Getter
@Setter
protected String delimiter = "\n";
protected String visit(final Source source) {
if (visitedResources.contains(source)) {
return null;
}
try {
return source.getBody().get();
} catch (final BuildException e) {
throw e;
} catch (final FileNotFoundException | NoSuchFileException ex) {
throw new BuildException("<green>"
+ getCanonicalForm(source.getPath().toFile().getAbsolutePath()) + "</green> not found");
} catch (final Throwable e) {
throw new BuildException(e);
}
}
/**
* Visit resource for concatenation.
*
* @param resource resource to visit
*
* @return concatenated result
*/
public BuildDetails visit(final Resource resource) {
final BuildDependency dependencyRoot = new BuildDependency(null);
dependencyRoot.setName(resource.getLocation());
final String contents = visit(resource, dependencyRoot);
final BuildDetails buildDetails = new BuildDetails();
buildDetails.setResult(contents);
buildDetails.setDependencies(dependencyRoot);
return buildDetails;
}
/**
* Visit resource with dependency.
*
* @param resource resource to concatenate
* @param resourceDependency object to record dependencies
*
* @return concatenated bytes
*/
public String visit(final Resource resource, final BuildDependency resourceDependency) {
logger.trace("Resource: {}", resource);
if (visitedResources.contains(resource)) {
return null;
}
final StringJoiner contentWriter = new StringJoiner("\n");
try {
callStack.enter(resource);
final Concatenator next = resource.adapt(ResourceManager.class)
.map(newResourceManager -> new Concatenator(newResourceManager, visitedResources))
.orElse(this);
logger.debug("Resource manager changed: {} -> {}", this, next);
visitDependencies(next, resource).forEach(childDependency -> {
final Resource parent = childDependency.getParent();
ofNullable(next.visit(parent, childDependency)).ifPresent(contentWriter::add);
resourceDependency.add(childDependency);
});
resource.adapt(Source.class).map(next::visit).ifPresent(contentWriter::add);
callStack.exit(resource);
visitedResources.add(resource);
return (0 < contentWriter.length()) ? contentWriter.toString() : null;
} catch (final BuildException e) {
throw e;
} catch (final Throwable ex) {
throw new BuildException(ex);
}
}
protected Collection<BuildDependency> visitDependencies(final Concatenator next,
final Resource resource) throws Exception {
final ResourceManager nextResourceManager = next.getResourceManager();
final List<Resource> dependencies = resource.getDependencies(nextResourceManager);
logger.debug("Dependencies of {}: {}", resource, dependencies);
final List<BuildDependency> resourceDependency = new ArrayList<>();
for (final Resource dependency : dependencies) {
final BuildDependency childDependency = new BuildDependency(dependency);
childDependency.setName(dependency.getLocation());
resourceDependency.add(childDependency);
}
return resourceDependency;
}
}