package org.jboss.windup.rules.files.condition; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.jboss.forge.furnace.util.Assert; import org.jboss.windup.config.GraphRewrite; import org.jboss.windup.config.Variables; import org.jboss.windup.config.condition.EvaluationStrategy; import org.jboss.windup.config.condition.NoopEvaluationStrategy; import org.jboss.windup.config.parameters.FrameContext; import org.jboss.windup.config.parameters.FrameCreationContext; import org.jboss.windup.config.parameters.ParameterizedGraphCondition; import org.jboss.windup.graph.model.FileLocationModel; import org.jboss.windup.graph.model.FileReferenceModel; import org.jboss.windup.graph.model.WindupVertexFrame; import org.jboss.windup.graph.model.resource.FileModel; import org.jboss.windup.graph.service.FileService; import org.jboss.windup.graph.service.GraphService; import org.jboss.windup.util.Logging; import org.ocpsoft.rewrite.config.ConditionBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; import org.ocpsoft.rewrite.param.DefaultParameterStore; import org.ocpsoft.rewrite.param.ParameterStore; import org.ocpsoft.rewrite.param.ParameterizedPatternResult; import org.ocpsoft.rewrite.param.RegexParameterizedPatternParser; import org.ocpsoft.rewrite.util.Maps; /** * Matches on file based upon parameterization. This condition is similar to {@link FileContent} condition, however is concerned only * with the file metadata and not the content inside. * <p/> * Example: * <p/> * <pre> * {@link File}.inFilesNamed("{filename}") * </pre> * * @author <a href="mailto:mbriskar@gmail.com">Matej Briskar</a> */ public class File extends ParameterizedGraphCondition { private static final Logger LOG = Logging.get(File.class); private RegexParameterizedPatternParser filenamePattern; public File() { } public static FileFrom from(String from) { FileFrom f = new FileFrom(); f.setFrom(from); return f; } public static File inFileNamed(String filenamePattern) { File f = new File(); f.filenamePattern = new RegexParameterizedPatternParser(filenamePattern); return f; } /** * Optionally specify the variable name to use for the output of this condition */ public ConditionBuilder as(String variable) { Assert.notNull(variable, "Variable name must not be null."); this.setOutputVariablesName(variable); return this; } @Override public Set<String> getRequiredParameterNames() { Set<String> result = new HashSet<>(); if (filenamePattern != null) result.addAll(filenamePattern.getRequiredParameterNames()); return result; } @Override public void setParameterStore(ParameterStore store) { if (filenamePattern != null) filenamePattern.setParameterStore(store); } @Override protected String getVarname() { return getOutputVariablesName(); } @Override @SuppressWarnings("unchecked") protected boolean evaluateAndPopulateValueStores(GraphRewrite event, EvaluationContext context, final FrameCreationContext frameCreationContext) { return evaluate(event, context, new EvaluationStrategy() { private LinkedHashMap<String, List<WindupVertexFrame>> variables; @Override @SuppressWarnings("rawtypes") public void modelMatched() { this.variables = new LinkedHashMap<>(); frameCreationContext.beginNew((Map) variables); } @Override public void modelSubmitted(WindupVertexFrame model) { Maps.addListValue(this.variables, getVarname(), model); } @Override public void modelSubmissionRejected() { frameCreationContext.rollback(); } }); } @Override protected boolean evaluateWithValueStore(GraphRewrite event, EvaluationContext context, final FrameContext frameContext) { boolean result = evaluate(event, context, new NoopEvaluationStrategy()); if (result == false) frameContext.reject(); return result; } private boolean evaluate(final GraphRewrite event, final EvaluationContext context, final EvaluationStrategy evaluationStrategy) { final ParameterStore store = DefaultParameterStore.getInstance(context); final GraphService<FileLocationModel> fileLocationService = new GraphService<>(event.getGraphContext(), FileLocationModel.class); // initialize the input List<FileModel> fileModels = new ArrayList<>(); fromInput(fileModels, event); fileNameInput(fileModels, event, store); allInput(fileModels, event, store); final List<FileLocationModel> results = new ArrayList<>(); for (final FileModel fileModel : fileModels) { if (fileModel.isDirectory()) continue; final ParameterizedPatternResult parsedFileNamePattern = filenamePattern.parse(fileModel.getFileName()); evaluationStrategy.modelMatched(); if (parsedFileNamePattern == null || parsedFileNamePattern.submit(event, context)) { // Use a file location model to make attaching hints possible FileLocationModel fileLocationModel = fileLocationService.create(); fileLocationModel.setFile(fileModel); fileLocationModel.setColumnNumber(1); fileLocationModel.setLineNumber(1); fileLocationModel.setLength(1); fileLocationModel.setSourceSnippit("File Match"); results.add(fileLocationModel); evaluationStrategy.modelSubmitted(fileLocationModel); } else { evaluationStrategy.modelSubmissionRejected(); } } setResults(event, getVarname(), results); return !results.isEmpty(); } public String toString() { StringBuilder builder = new StringBuilder(); builder.append(this.getClass().getSimpleName()); builder.append(".from(").append(getInputVariablesName()).append(")"); builder.append(".inFilesNamed(").append(filenamePattern).append(")"); builder.append(".as(").append(getVarname()).append(")"); return builder.toString(); } /** * Generating the input vertices is quite complex. Therefore there are multiple methods that handles the input vertices based on the attribute * specified in specific order. This method handles the {@link File#from(String)} attribute. */ private void fromInput(List<FileModel> vertices, GraphRewrite event) { if (vertices.isEmpty() && StringUtils.isNotBlank(getInputVariablesName())) { for (WindupVertexFrame windupVertexFrame : Variables.instance(event).findVariable(getInputVariablesName())) { if (windupVertexFrame instanceof FileModel) vertices.add((FileModel) windupVertexFrame); if (windupVertexFrame instanceof FileReferenceModel) vertices.add(((FileReferenceModel) windupVertexFrame).getFile()); } } } /** * Generating the input vertices is quite complex. Therefore there are multiple methods that handles the input vertices based on the attribute * specified in specific order. This method handles the {@link File#inFileNamed(String)} attribute. */ private void fileNameInput(List<FileModel> vertices, GraphRewrite event, ParameterStore store) { if (this.filenamePattern != null) { Pattern filenameRegex = filenamePattern.getCompiledPattern(store); //in case the filename is the first operation generating result, we can use the graph regex index. That's why we distinguish that in this clause if (vertices.isEmpty() && StringUtils.isBlank(getInputVariablesName())) { FileService fileModelService = new FileService(event.getGraphContext()); for (FileModel fileModel : fileModelService.findByFilenameRegex(filenameRegex.pattern())) { vertices.add(fileModel); } } else { for (FileModel vertex : vertices) { if (!filenameRegex.matcher(vertex.getFileName()).matches()) { vertices.remove(vertex); } } } } } /** * Generating the input vertices is quite complex. Therefore there are multiple methods that handles the input vertices * based on the attribute specified in specific order. This method generates all the vertices if there is no other way how to handle * the input. */ private void allInput(List<FileModel> vertices, GraphRewrite event, ParameterStore store) { if (StringUtils.isBlank(getInputVariablesName()) && this.filenamePattern == null) { FileService fileModelService = new FileService(event.getGraphContext()); for (FileModel fileModel : fileModelService.findAll()) { vertices.add(fileModel); } } } public void setFilenamePattern(RegexParameterizedPatternParser filenamePattern) { this.filenamePattern = filenamePattern; } }