package org.revapi.basic; import static java.util.stream.Collectors.toMap; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.revapi.Difference; import org.revapi.Element; /** * A helper class to {@link org.revapi.basic.AbstractDifferenceReferringTransform} that defines the match of * a configuration element and a difference. * * @author Lukas Krejci * @since 0.1 */ public abstract class DifferenceMatchRecipe { final ModelNode config; final boolean regex; final String code; final Pattern codeRegex; final String oldElement; final Pattern oldElementRegex; final String newElement; final Pattern newElementRegex; final Map<String, String> attachments; final Map<String, Pattern> attachmentRegexes; protected DifferenceMatchRecipe(ModelNode config, String... additionalReservedProperties) { if (!config.has("code")) { throw new IllegalArgumentException("Difference code has to be specified."); } Set<String> reservedProperties = new HashSet<>(4 + additionalReservedProperties.length); reservedProperties.add("regex"); reservedProperties.add("code"); reservedProperties.add("old"); reservedProperties.add("new"); for (String p : additionalReservedProperties) { reservedProperties.add(p); } regex = config.has("regex") && config.get("regex").asBoolean(); code = config.get("code").asString(); codeRegex = regex ? Pattern.compile(code) : null; oldElement = getElement(config.get("old")); oldElementRegex = regex && oldElement != null ? Pattern.compile(oldElement) : null; newElement = getElement(config.get("new")); newElementRegex = regex && newElement != null ? Pattern.compile(newElement) : null; attachments = getAttachments(config, reservedProperties); if (regex) { attachmentRegexes = attachments.entrySet().stream() .collect(toMap(Map.Entry::getKey, e -> Pattern.compile(e.getValue()))); } else { attachmentRegexes = null; } this.config = config; } public boolean matches(Difference difference, Element oldElement, Element newElement) { if (regex) { boolean baseMatch = codeRegex.matcher(difference.code).matches() && (oldElementRegex == null || oldElementRegex.matcher(oldElement.getFullHumanReadableString()).matches()) && (newElementRegex == null || newElementRegex.matcher(newElement.getFullHumanReadableString()).matches()); if (!baseMatch) { return false; } else { for (Map.Entry<String, String> e: difference.attachments.entrySet()) { String key = e.getKey(); String val = e.getValue(); Pattern match = attachmentRegexes.get(key); if (match != null && !match.matcher(val).matches()) { return false; } } return true; } } else { boolean baseMatch = code.equals(difference.code) && (this.oldElement == null || this.oldElement.equals(oldElement.getFullHumanReadableString())) && (this.newElement == null || this.newElement.equals(newElement.getFullHumanReadableString())); if (!baseMatch) { return false; } else { for (Map.Entry<String, String> e : difference.attachments.entrySet()) { String key = e.getKey(); String val = e.getValue(); String match = attachments.get(key); if (match != null && !match.equals(val)) { return false; } } return true; } } } public abstract Difference transformMatching(Difference difference, Element oldElement, Element newElement); private static String getElement(ModelNode elementRoot) { if (!elementRoot.isDefined()) { return null; } return elementRoot.getType() == ModelType.STRING ? elementRoot.asString() : null; } private static Map<String, String> getAttachments(ModelNode elementRoot, Set<String> reservedProperties) { if (!elementRoot.isDefined()) { return Collections.emptyMap(); } if (elementRoot.getType() != ModelType.OBJECT) { return Collections.emptyMap(); } else { Set<String> keys = elementRoot.keys(); Map<String, String> ret = new HashMap<>(keys.size()); for (String key: keys) { if (!reservedProperties.contains(key)) { ret.put(key, elementRoot.get(key).asString()); } } return ret; } } }