package org.jboss.windup.rules.apps.xml.condition; 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.parameters.FrameContext; import org.jboss.windup.config.parameters.FrameCreationContext; import org.jboss.windup.config.parameters.ParameterizedGraphCondition; import org.jboss.windup.graph.GraphContext; import org.jboss.windup.graph.model.FileReferenceModel; import org.jboss.windup.graph.model.WindupVertexFrame; import org.jboss.windup.graph.service.GraphService; import org.jboss.windup.rules.apps.xml.condition.validators.XmlCacheValidator; import org.jboss.windup.rules.apps.xml.condition.validators.XmlFileDtdValidator; import org.jboss.windup.rules.apps.xml.condition.validators.XmlFileNameValidator; import org.jboss.windup.rules.apps.xml.condition.validators.XmlFileXpathValidator; import org.jboss.windup.rules.apps.xml.model.XmlFileModel; import org.jboss.windup.rules.apps.xml.model.XmlTypeReferenceModel; import org.jboss.windup.util.ExecutionStatistics; import org.jboss.windup.util.exception.WindupException; import org.ocpsoft.rewrite.config.Condition; import org.ocpsoft.rewrite.config.ConditionBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; import org.ocpsoft.rewrite.param.DefaultParameterValueStore; import org.ocpsoft.rewrite.param.Parameter; import org.ocpsoft.rewrite.param.ParameterStore; import org.ocpsoft.rewrite.param.ParameterValueStore; import org.ocpsoft.rewrite.param.RegexParameterizedPatternParser; import org.ocpsoft.rewrite.util.Maps; import javax.xml.xpath.XPathExpression; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Handles matching on {@link XmlFileModel} objects and creating/returning {@link XmlTypeReferenceModel} or {@link XmlFileModel} objects on the matching nodes/whole files. */ public class XmlFile extends ParameterizedGraphCondition implements XmlFileDTD, XmlFileIn, XmlFileNamespace, XmlFileResult, XmlFileXpath { public XmlFile() { } XmlCacheValidator cacheValidator= new XmlCacheValidator(); XmlFileNameValidator fileNameValidator= new XmlFileNameValidator(); XmlFileDtdValidator dtdValidator = new XmlFileDtdValidator(); XmlFileXpathValidator xpathValidator = new XmlFileXpathValidator(); public void setXpathResultMatch(String xpathResultMatch) { xpathValidator.setXpathResult(xpathResultMatch); } private XmlFile(String xpath) { setXpath(xpath); } public String getXpathString() { return xpathValidator.getXpathString(); } /** * Create a new {@link XmlFile} {@link Condition}. */ public static XmlFileXpath matchesXpath(String xpath) { return new XmlFile(xpath); } public XmlFileDTD andDTDPublicId(String publicIdRegex) { this.dtdValidator.setPublicId(publicIdRegex); return this; } /** * Create a new {@link XmlFile} that matches on the provided DTD namespace. */ public static XmlFileDTD withDTDSystemId(String dtdNamespace) { XmlFile xmlFile = new XmlFile(); xmlFile.dtdValidator.setSystemId(dtdNamespace); return xmlFile; } public XmlFileDTD andDTDSystemId(String dtdNamespace) { dtdValidator.setSystemId(dtdNamespace); return this; } /** * Output the results using the provided variable name. */ public ConditionBuilder as(String variable) { Assert.notNull(variable, "Variable name must not be null."); this.setOutputVariablesName(variable); return this; } /** * Scan only files that match the given file name. */ public XmlFileIn inFile(String fileName) { this.fileNameValidator.setFileNameRegex(fileName); return this; } /** * Only return results that match the given regex. */ public XmlFileResult resultMatches(String regex) { this.xpathValidator.setXpathResult(regex); return this; } public XPathExpression getXPathExpression() { return this.xpathValidator.getXpathExpression(); } public RegexParameterizedPatternParser getInFilePattern() { return this.fileNameValidator.getFileNamePattern(); } public String getPublicId() { return this.dtdValidator.getPublicId(); } /** * Specify the name of the variables to base this query on. * * @param fromVariable * @return */ public static XmlFileFrom from(String fromVariable) { return new XmlFileFrom(fromVariable); } @Override public void setParameterStore(ParameterStore store) { this.xpathValidator.setParameterStore(store); } @Override public Set<String> getRequiredParameterNames() { Set<String> result = new HashSet<>(); result.addAll(xpathValidator.getRequiredParamaterNames()); result.addAll(fileNameValidator.getRequiredParamaterNames()); return result; } @Override protected String getVarname() { return getOutputVariablesName(); } @Override @SuppressWarnings("unchecked") protected boolean evaluateAndPopulateValueStores(final GraphRewrite event, final EvaluationContext context, final FrameCreationContext frameCreationContext) { return evaluate(event, context, new XmlFileEvaluationStrategy() { private LinkedHashMap<String, List<WindupVertexFrame>> variables; @Override public void modelMatched() { this.variables = new LinkedHashMap<>(); frameCreationContext.beginNew((Map) this.variables); } @Override @SuppressWarnings("rawtypes") public void modelSubmitted(WindupVertexFrame model) { Maps.addListValue(this.variables, getVarname(), model); } @Override public boolean submitValue(Parameter<?> parameter, String value) { ParameterValueStore valueStore = DefaultParameterValueStore.getInstance(context); return valueStore.submit(event, context, parameter, value); } @Override public void modelSubmissionRejected() { if (variables != null) { this.variables = null; frameCreationContext.rollback(); } } }); } public interface XmlFileEvaluationStrategy extends EvaluationStrategy { boolean submitValue(Parameter<?> parameter, String value); } @Override protected boolean evaluateWithValueStore(final GraphRewrite event, final EvaluationContext context, final FrameContext frameContext) { boolean result = evaluate(event, context, new XmlFileEvaluationStrategy() { @Override public void modelMatched() { } @Override public void modelSubmitted(WindupVertexFrame model) { } @Override public boolean submitValue(Parameter<?> parameter, String value) { ParameterValueStore valueStore = DefaultParameterValueStore.getInstance(context); String existingValue = valueStore.retrieve(parameter); if (existingValue == null) { return valueStore.submit(event, context, parameter, value); } else { return valueStore.isValid(event, context, parameter, value); } } @Override public void modelSubmissionRejected() { } }); if (!result) frameContext.reject(); return result; } private boolean evaluate(final GraphRewrite event, final EvaluationContext context, final XmlFileEvaluationStrategy evaluationStrategy) { try { ExecutionStatistics.get().begin("XmlFile.evaluate"); initValidators(event,context,evaluationStrategy); final List<WindupVertexFrame> finalResults = new ArrayList<>(); final GraphContext graphContext = event.getGraphContext(); Iterable<? extends WindupVertexFrame> startVertices = getStartingVertices(event,graphContext); for (WindupVertexFrame iterated : startVertices) { XmlFileModel xml = getXmlFileModelFromVertex(iterated); if(xmlFilePassRestrictions(event,context,xml)) { registerAndSubmitResultsFor(xml, finalResults,evaluationStrategy); } } setResults(event, getOutputVariablesName(), finalResults); return !finalResults.isEmpty(); } finally { ExecutionStatistics.get().end("XmlFile.evaluate"); } } private void initValidators(GraphRewrite event, EvaluationContext context, XmlFileEvaluationStrategy evaluationStrategy) { xpathValidator.setEvaluationStrategy(evaluationStrategy); cacheValidator.clear(); } private Iterable<? extends WindupVertexFrame> getStartingVertices(GraphRewrite event,GraphContext graphContext) { GraphService<XmlFileModel> xmlResourceService = new GraphService<>(graphContext, XmlFileModel.class); Iterable<? extends WindupVertexFrame> allXmls; if (getInputVariablesName() == null || getInputVariablesName().isEmpty()) { allXmls = xmlResourceService.findAll(); } else { allXmls = Variables.instance(event).findVariable(getInputVariablesName()); } return allXmls; } private void registerAndSubmitResultsFor(XmlFileModel xml, List<WindupVertexFrame> results, EvaluationStrategy evaluationStrategy) { final List<WindupVertexFrame> xpathResults = xpathValidator.getAndClearResultLocations(); if(xpathResults.isEmpty()) { evaluationStrategy.modelMatched(); evaluationStrategy.modelSubmitted(xml); results.add(xml); } else { //these were already submitted by XpathValidator results.addAll(xpathResults); } } private XmlFileModel getXmlFileModelFromVertex(WindupVertexFrame vertexFrame) { final XmlFileModel xml; if (vertexFrame instanceof FileReferenceModel) { xml = (XmlFileModel) ((FileReferenceModel) vertexFrame).getFile(); } else if (vertexFrame instanceof XmlFileModel) { xml = (XmlFileModel) vertexFrame; } else { throw new WindupException("XmlFile was called on the wrong graph type ( " + vertexFrame.toPrettyString() + ")"); } return xml; } private boolean xmlFilePassRestrictions(final GraphRewrite event,final EvaluationContext context,XmlFileModel xml) { boolean validation = cacheValidator.isValid(event,context,xml); validation = validation && fileNameValidator.isValid(event,context,xml); validation = validation && dtdValidator.isValid(event,context,xml); validation = validation && xpathValidator.isValid(event,context,xml); return validation; } public XmlFileNamespace namespace(String prefix, String url) { this.xpathValidator.addNamespace(prefix, url); return this; } public void setXpath(String xpath) { this.xpathValidator.setXpathString(xpath); } public String toString() { StringBuilder builder = new StringBuilder(); builder.append("XmlFile"); if (getInputVariablesName() != null) { builder.append(".inputVariable(" + getInputVariablesName() + ")"); } if (xpathValidator.getXpathString() != null) { builder.append(".matches(" + xpathValidator.getXpathString() + ")"); } if (fileNameValidator.getFileNamePattern() != null) { builder.append(".inFile(" + fileNameValidator.getFileNamePattern().toString() + ")"); } if (dtdValidator.getPublicId() != null) { builder.append(".withDTDPublicId(" + dtdValidator.getPublicId() + ")"); } builder.append(".as(" + getOutputVariablesName() + ")"); return builder.toString(); } }