package org.revapi.basic; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.revapi.AnalysisContext; import org.revapi.Difference; import org.revapi.DifferenceTransform; import org.revapi.Element; /** * @author Lukas Krejci * @since 0.1 */ public abstract class AbstractDifferenceReferringTransform<Recipe extends DifferenceMatchRecipe, ConfigContext> implements DifferenceTransform<Element> { private final String[] propertyPrefix; private final String propertyPrefixAsString; private Collection<Recipe> configuredRecipes; private Pattern[] codes; protected AbstractDifferenceReferringTransform(@Nonnull String... propertyPrefix) { this.propertyPrefix = propertyPrefix; if (propertyPrefix.length == 0) { throw new IllegalArgumentException( "The transformation must have a non-empty path in the configuration JSON"); } StringBuilder bld = new StringBuilder(propertyPrefix[0]); for (int i = 1; i < propertyPrefix.length; ++i) { bld.append(".").append(propertyPrefix[i]); } propertyPrefixAsString = bld.toString(); } @Nonnull @Override public Pattern[] getDifferenceCodePatterns() { return codes; } @Nullable protected abstract ConfigContext initConfiguration(); @Nonnull protected abstract Recipe newRecipe(@Nullable ConfigContext context, ModelNode configNode) throws IllegalArgumentException; @Override public final void initialize(@Nonnull AnalysisContext analysisContext) { ConfigContext ctx = initConfiguration(); configuredRecipes = new ArrayList<>(); int idx = 0; ModelNode myNode = analysisContext.getConfiguration().get(propertyPrefix); if (myNode.getType() != ModelType.LIST) { this.codes = new Pattern[0]; return; } List<Pattern> codes = new ArrayList<>(); for (ModelNode config : myNode.asList()) { try { Recipe recipe = newRecipe(ctx, config); codes.add( recipe.codeRegex == null ? Pattern.compile("^" + Pattern.quote(recipe.code) + "$") : recipe.codeRegex); configuredRecipes.add(recipe); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( "Property " + propertyPrefixAsString + "[" + idx + "] is not valid: " + e.getMessage()); } } this.codes = codes.toArray(new Pattern[codes.size()]); } @Nullable @Override public final Difference transform(@Nullable Element oldElement, @Nullable Element newElement, @Nonnull Difference difference) { if (configuredRecipes == null) { return difference; } for (Recipe r : configuredRecipes) { if (r.matches(difference, oldElement, newElement)) { return r.transformMatching(difference, oldElement, newElement); } } return difference; } }