package org.jboss.windup.rules.apps.xml.condition; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; /** * Converts an XPath from a standard xpath, to one that calls Windup's builtin functions for hooking into the XPath execution lifecycle. */ public class XmlFileXPathTransformer { private static final String WINDUP_MATCHES_FUNCTION_PREFIX = "windup:matches("; /** * Performs the conversion from standard XPath to xpath with parameterization support. */ public static String transformXPath(String originalXPath) { // use a list to maintain the multiple joined xqueries (if there are multiple queries joined with the "|" operator) List<StringBuilder> compiledXPaths = new ArrayList<>(1); int frameIdx = -1; boolean inQuote = false; int conditionLevel = 0; char startQuoteChar = 0; StringBuilder currentXPath = new StringBuilder(); compiledXPaths.add(currentXPath); for (int i = 0; i < originalXPath.length(); i++) { char curChar = originalXPath.charAt(i); if (!inQuote && curChar == '[') { frameIdx++; conditionLevel++; currentXPath.append("[windup:startFrame(").append(frameIdx).append(") and windup:evaluate(").append(frameIdx).append(", "); } else if (!inQuote && curChar == ']') { conditionLevel--; currentXPath.append(")]"); } else if (!inQuote && conditionLevel == 0 && curChar == '|') { // joining multiple xqueries currentXPath = new StringBuilder(); compiledXPaths.add(currentXPath); } else { if (inQuote && curChar == startQuoteChar) { inQuote = false; startQuoteChar = 0; } else if (curChar == '"' || curChar == '\'') { inQuote = true; startQuoteChar = curChar; } if (!inQuote && originalXPath.startsWith(WINDUP_MATCHES_FUNCTION_PREFIX, i)) { i += (WINDUP_MATCHES_FUNCTION_PREFIX.length() - 1); currentXPath.append("windup:matches(").append(frameIdx).append(", "); } else { currentXPath.append(curChar); } } } Pattern leadingAndTrailingWhitespace = Pattern.compile("(\\s*)(.*?)(\\s*)"); StringBuilder finalResult = new StringBuilder(); for (StringBuilder compiledXPath : compiledXPaths) { if (StringUtils.isNotBlank(compiledXPath)) { Matcher whitespaceMatcher = leadingAndTrailingWhitespace.matcher(compiledXPath); if (!whitespaceMatcher.matches()) continue; compiledXPath = new StringBuilder(); compiledXPath.append(whitespaceMatcher.group(1)); compiledXPath.append(whitespaceMatcher.group(2)); compiledXPath.append("/self::node()[windup:persist(").append(frameIdx).append(", ").append(".)]"); compiledXPath.append(whitespaceMatcher.group(3)); if (StringUtils.isNotBlank(finalResult)) finalResult.append("|"); finalResult.append(compiledXPath); } } return finalResult.toString(); } }