package org.jboss.windup.rules.files; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Logger; import java.util.regex.Pattern; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.jboss.forge.furnace.util.Assert; import org.jboss.windup.config.GraphRewrite; import org.jboss.windup.config.GraphRule; import org.jboss.windup.config.PreRulesetEvaluation; import org.jboss.windup.config.phase.ArchiveMetadataExtractionPhase; import org.jboss.windup.graph.model.WindupVertexFrame; import org.jboss.windup.graph.model.resource.FileModel; import org.jboss.windup.graph.model.resource.FileModel.OnParseError; import org.jboss.windup.graph.service.FileService; import org.jboss.windup.graph.service.GraphService; import org.jboss.windup.util.Logging; import org.ocpsoft.rewrite.context.EvaluationContext; /** * Maps file extensions to {@link WindupVertexFrame} types. Mappings are always applied during the * {@link ArchiveMetadataExtractionPhase} phase, no matter where this rule appears in the pipeline. The following example * demonstrates how to match files ending with *.xml to a frame type: * * <pre> * {@link FileMapping}.from(".*\\.xml").to({@link XmlFileModel}.class) * </pre> * * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> * */ public class FileMapping extends GraphRule implements PreRulesetEvaluation, FileMappingFrom, FileMappingTo, FileMappingWithID { private static final Logger LOG = Logging.get(FileMapping.class); private final Pattern pattern; private final List<Class<? extends WindupVertexFrame>> types = new ArrayList<>(); private OnParseError onParseError = OnParseError.WARN; private String id; private FileMapping(Pattern pattern) { this.pattern = pattern; String normalizedPattern = StringUtils.replacePattern(pattern.pattern(), "\\s", "_"); normalizedPattern = StringUtils.substring(normalizedPattern, 0, 10); this.id = this.getClass().getSimpleName()+ "_" + normalizedPattern + "_" + RandomStringUtils.randomAlphanumeric(2); } @Override public FileMappingWithID withId(String ruleID) { this.id = ruleID; return this; } /** * Create a new {@link FileMapping} */ public static FileMappingFrom from(String regex) { return new FileMapping(Pattern.compile(regex)); } @Override public FileMappingTo to(Class<? extends WindupVertexFrame> type) { Assert.notNull(type, "Model type must not be null."); this.types.add(type); return this; } @Override @SuppressWarnings("unchecked") public FileMappingTo to(Class<? extends WindupVertexFrame>... types) { Assert.notNull(types, "Model type list must not be null."); this.types.addAll(Arrays.asList(types)); return this; } @Override public FileMappingTo onParseError(OnParseError onParseError) { this.onParseError = onParseError; return this; } @Override public void preRulesetEvaluation(GraphRewrite event) { LOG.info("Added " + toString()); for (Class<? extends WindupVertexFrame> type : types) { addMapping(event, pattern.pattern(), type); } /* * Handle mapping any files that were directly inserted in the graph before this executed. */ Map<String, List<Class<? extends WindupVertexFrame>>> mappings = FileMapping .getMappings(event); FileService fileService = new FileService(event.getGraphContext()); for (Entry<String, List<Class<? extends WindupVertexFrame>>> entry : mappings.entrySet()) { String pattern = entry.getKey(); List<Class<? extends WindupVertexFrame>> types = entry.getValue(); Iterable<FileModel> models = fileService.findAllByPropertyMatchingRegex(FileModel.FILE_PATH, pattern); for (FileModel model : models) { if (model.isDirectory()) continue; for (Class<? extends WindupVertexFrame> type : types) GraphService.addTypeToModel(event.getGraphContext(), model, type); if (this.onParseError == OnParseError.IGNORE) model.setOnParseError(OnParseError.IGNORE); LOG.info("Mapped file [" + model.getFilePath() + "] matching pattern [" + pattern + "] to the following [" + types.size() + "] types: " + types); } } } /** * Get the list of pattern to {@link WindupVertexFrame} type mappings for the {@link GraphRewrite} event. */ @SuppressWarnings("unchecked") public static Map<String, List<Class<? extends WindupVertexFrame>>> getMappings(GraphRewrite event) { Map<String, List<Class<? extends WindupVertexFrame>>> map = (Map<String, List<Class<? extends WindupVertexFrame>>>) event .getRewriteContext().get(FileMapping.class); if (map == null) { map = new HashMap<>(); event.getRewriteContext().put(FileMapping.class, map); } return map; } /** * Get the list of {@link WindupVertexFrame} type mappings for the given pattern and {@link GraphRewrite} event. */ public static List<Class<? extends WindupVertexFrame>> getMappings(GraphRewrite event, String pattern) { Map<String, List<Class<? extends WindupVertexFrame>>> mappings = getMappings(event); List<Class<? extends WindupVertexFrame>> result = mappings.get(pattern); if (result == null) { result = new ArrayList<>(); mappings.put(pattern, result); } return result; } /** * Add a {@link WindupVertexFrame} type to the list of mappings for the given pattern and {@link GraphRewrite} * event. */ public static void addMapping(GraphRewrite event, String pattern, Class<? extends WindupVertexFrame> type) { getMappings(event, pattern).add(type); } @Override public boolean evaluate(GraphRewrite event, EvaluationContext context) { return true; } @Override public void perform(GraphRewrite event, EvaluationContext context) { // No-op } @Override public String getId() { return this.id; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(getClass().getName()); builder.append(".from(").append(pattern).append(")"); builder.append(".to(").append(types).append(")"); return builder.toString(); } }