package org.jboss.windup.reporting.rules.rendering;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.forge.furnace.Furnace;
import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.addons.AddonDependency;
import org.jboss.forge.furnace.addons.AddonFilter;
import org.jboss.windup.config.AbstractRuleProvider;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.loader.RuleLoaderContext;
import org.jboss.windup.config.metadata.RuleMetadata;
import org.jboss.windup.config.operation.GraphOperation;
import org.jboss.windup.config.phase.ReportRenderingPhase;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.WindupConfigurationModel;
import org.jboss.windup.graph.service.WindupConfigurationService;
import org.jboss.windup.reporting.service.ReportService;
import org.ocpsoft.rewrite.config.Configuration;
import org.ocpsoft.rewrite.config.ConfigurationBuilder;
import org.ocpsoft.rewrite.context.EvaluationContext;
@RuleMetadata(phase = ReportRenderingPhase.class)
public class CssJsResourceRenderingRuleProvider extends AbstractRuleProvider
{
@Inject
private Addon addon;
@Inject
private Furnace furnace;
@Override
public Configuration getConfiguration(RuleLoaderContext ruleLoaderContext)
{
GraphOperation copyCssOperation = new GraphOperation()
{
@Override
public void perform(GraphRewrite event, EvaluationContext context)
{
final WindupConfigurationModel cfg = WindupConfigurationService.getConfigurationModel(event
.getGraphContext());
copyCssResourcesToOutput(event.getGraphContext());
}
@Override
public String toString()
{
return "CopyCSSToOutput";
}
};
Configuration configuration = ConfigurationBuilder.begin()
.addRule()
.perform(copyCssOperation);
return configuration;
}
private void copyCssResourcesToOutput(GraphContext context)
{
ReportService reportService = new ReportService(context);
Path outputPath = reportService.getReportDirectory().resolve("resources");
// iterate through the addons to scan
for (Addon addonToScan : getAddonsToScan())
{
List<File> addonResources = addon.getRepository().getAddonResources(addonToScan.getId());
for (File addonResource : addonResources)
{
try
{
if (addonResource.isDirectory())
{
Path addonReportsResourcesPath = addonResource.toPath().resolve("reports").resolve("resources");
if (Files.isDirectory(addonReportsResourcesPath))
{
recursePath(addonReportsResourcesPath, outputPath);
}
}
else
{
try (FileSystem fs = FileSystems.newFileSystem(addonResource.toPath(),
addonToScan.getClassLoader()))
{
Path p = fs.getPath("reports", "resources");
try
{
recursePath(p, outputPath);
}
catch (NoSuchFileException e)
{
// ignore ... this just means this archive did not contain report resources
}
}
}
}
catch (Exception e)
{
throw new RuntimeException("Exception reading resource.", e);
}
}
}
}
private void recursePath(final Path path, final Path resultPath) throws IOException
{
Files.walkFileTree(path, new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
String relativePath = StringUtils.substringAfter(file.toString(), path.toString());
// needed on windows, as for some reason the path from a zip still uses s'/' sometimes
relativePath = FilenameUtils.separatorsToSystem(relativePath);
relativePath = StringUtils.removeStart(relativePath, File.separator);
Path resultFile = resultPath.resolve(relativePath);
FileUtils.forceMkdir(resultFile.getParent().toFile());
Files.copy(file, resultFile, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Get all {@link Addon} instances that depend on the reporting addon
*/
private Set<Addon> getAddonsToScan()
{
AddonFilter filter = new AddonFilter()
{
@Override
public boolean accept(Addon addon)
{
// make sure to include ourselves as well (even though we don't technically depend on ourselves)
return addonDependsOnReporting(addon) || addon.equals(CssJsResourceRenderingRuleProvider.this.addon);
}
};
return furnace.getAddonRegistry().getAddons(filter);
}
/**
* Returns true if the addon depends on reporting.
*/
private boolean addonDependsOnReporting(Addon addon)
{
for (AddonDependency dep : addon.getDependencies())
{
if (dep.getDependency().equals(this.addon))
{
return true;
}
boolean subDep = addonDependsOnReporting(dep.getDependency());
if (subDep)
{
return true;
}
}
return false;
}
}