package org.jboss.windup.config.loader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.jboss.forge.furnace.proxy.Proxies;
import org.jboss.forge.furnace.services.Imported;
import org.jboss.windup.config.AbstractRuleProvider;
import org.jboss.windup.config.RuleProvider;
import org.jboss.windup.config.metadata.RuleProviderRegistry;
import org.jboss.windup.config.phase.RulePhase;
import org.jboss.windup.util.ServiceLogger;
import org.jboss.windup.util.exception.WindupException;
import org.ocpsoft.rewrite.bind.Evaluation;
import org.ocpsoft.rewrite.config.Condition;
import org.ocpsoft.rewrite.config.ConditionVisit;
import org.ocpsoft.rewrite.config.Configuration;
import org.ocpsoft.rewrite.config.ConfigurationBuilder;
import org.ocpsoft.rewrite.config.Operation;
import org.ocpsoft.rewrite.config.OperationVisit;
import org.ocpsoft.rewrite.config.ParameterizedCallback;
import org.ocpsoft.rewrite.config.ParameterizedConditionVisitor;
import org.ocpsoft.rewrite.config.ParameterizedOperationVisitor;
import org.ocpsoft.rewrite.config.Rule;
import org.ocpsoft.rewrite.config.RuleBuilder;
import org.ocpsoft.rewrite.param.ConfigurableParameter;
import org.ocpsoft.rewrite.param.DefaultParameter;
import org.ocpsoft.rewrite.param.Parameter;
import org.ocpsoft.rewrite.param.ParameterStore;
import org.ocpsoft.rewrite.param.Parameterized;
import org.ocpsoft.rewrite.param.ParameterizedRule;
import org.ocpsoft.rewrite.util.Visitor;
public class RuleLoaderImpl implements RuleLoader
{
public static Logger LOG = Logger.getLogger(RuleLoaderImpl.class.getName());
@Inject
private Imported<RuleProviderLoader> loaders;
public RuleLoaderImpl()
{
}
@Override
public RuleProviderRegistry loadConfiguration(RuleLoaderContext ruleLoaderContext)
{
return build(ruleLoaderContext);
}
/**
* Prints all of the {@link RulePhase} objects in the order that they should execute. This is primarily for debug purposes and should be called
* before the entire {@link RuleProvider} list is sorted, as this will allow us to print the {@link RulePhase} list without the risk of
* user-introduced cycles making the sort impossible.
*/
private void printRulePhases(List<RuleProvider> allProviders)
{
List<RuleProvider> unsortedPhases = new ArrayList<>();
for (RuleProvider provider : allProviders)
{
if (provider instanceof RulePhase)
unsortedPhases.add(provider);
}
List<RuleProvider> sortedPhases = RuleProviderSorter.sort(unsortedPhases);
StringBuilder rulePhaseSB = new StringBuilder();
for (RuleProvider phase : sortedPhases)
{
Class<?> unproxiedClass = Proxies.unwrap(phase).getClass();
rulePhaseSB.append("\tPhase: ").append(unproxiedClass.getSimpleName()).append("\n");
}
LOG.info("Rule Phases: [\n" + rulePhaseSB.toString() + "]");
}
private void checkForDuplicateProviders(List<RuleProvider> providers)
{
/*
* We are using a map so that we can easily pull out the previous value later (in the case of a duplicate)
*/
Map<RuleProvider, RuleProvider> duplicates = new HashMap<>(providers.size());
for (RuleProvider provider : providers)
{
RuleProvider previousProvider = duplicates.get(provider);
if (previousProvider != null)
{
String typeMessage;
String currentProviderOrigin = provider.getMetadata().getOrigin();
String previousProviderOrigin = previousProvider.getMetadata().getOrigin();
if (previousProvider.getClass().equals(provider.getClass()))
{
typeMessage = " (type: " + previousProviderOrigin + " and " + currentProviderOrigin + ")";
}
else
{
typeMessage = " (types: " + Proxies.unwrapProxyClassName(previousProvider.getClass()) + " at " + previousProviderOrigin
+ " and " + Proxies.unwrapProxyClassName(provider.getClass()) + " at " + currentProviderOrigin + ")";
}
throw new WindupException("Found two providers with the same id: " + provider.getMetadata().getID() + typeMessage);
}
duplicates.put(provider, provider);
}
}
private List<RuleProvider> getProviders(RuleLoaderContext ruleLoaderContext)
{
LOG.info("Starting provider load...");
List<RuleProvider> unsortedProviders = new ArrayList<>();
for (RuleProviderLoader loader : loaders)
{
if (ruleLoaderContext.isFileBasedRulesOnly() && !loader.isFileBased())
continue;
unsortedProviders.addAll(loader.getProviders(ruleLoaderContext));
}
LOG.info("Loaded, now sorting, etc");
checkForDuplicateProviders(unsortedProviders);
printRulePhases(unsortedProviders);
List<RuleProvider> sortedProviders = RuleProviderSorter.sort(unsortedProviders);
ServiceLogger.logLoadedServices(LOG, RuleProvider.class, sortedProviders);
LOG.info("Finished provider load");
return Collections.unmodifiableList(sortedProviders);
}
private RuleProviderRegistry build(RuleLoaderContext ruleLoaderContext)
{
List<Rule> allRules = new ArrayList<>(2000); // estimate of how many rules we will likely see
List<RuleProvider> providers = getProviders(ruleLoaderContext);
RuleProviderRegistry registry = new RuleProviderRegistry();
registry.setProviders(providers);
Map<RuleKey, Rule> overrideRules = new HashMap<>();
for (RuleProvider provider : providers)
{
if (!provider.getMetadata().isOverrideProvider())
continue;
Configuration cfg = provider.getConfiguration(null);
List<Rule> rules = cfg.getRules();
for (Rule rule : rules)
overrideRules.put(new RuleKey(provider.getMetadata().getID(), rule.getId()), rule);
}
for (RuleProvider provider : providers)
{
if (ruleLoaderContext.getRuleProviderFilter() != null)
{
boolean accepted = ruleLoaderContext.getRuleProviderFilter().accept(provider);
LOG.info((accepted ? "Accepted" : "Skipped") + ": [" + provider + "] by filter [" + ruleLoaderContext.getRuleProviderFilter() + "]");
if (!accepted)
continue;
}
// these are not used directly... they only override others
if (provider.getMetadata().isOverrideProvider())
continue;
Configuration cfg = provider.getConfiguration(ruleLoaderContext);
// copy it to allow for the option of modification
List<Rule> rules = new ArrayList<>(cfg.getRules());
ListIterator<Rule> ruleIterator = rules.listIterator();
while (ruleIterator.hasNext())
{
Rule rule = ruleIterator.next();
Rule overrideRule = overrideRules.get(new RuleKey(provider.getMetadata().getID(), rule.getId()));
if (overrideRule != null)
{
LOG.info("Replacing rule " + rule.getId() + " with a user override!");
ruleIterator.set(overrideRule);
}
}
registry.setRules(provider, rules);
int i = 0;
for (final Rule rule : rules)
{
i++;
AbstractRuleProvider.enhanceRuleMetadata(provider, rule);
if (rule instanceof RuleBuilder && StringUtils.isBlank(rule.getId()))
{
((RuleBuilder) rule).withId(generatedRuleID(provider, rule, i));
}
allRules.add(rule);
if (rule instanceof ParameterizedRule)
{
ParameterizedCallback callback = new ParameterizedCallback()
{
@Override
public void call(Parameterized parameterized)
{
Set<String> names = parameterized.getRequiredParameterNames();
ParameterStore store = ((ParameterizedRule) rule).getParameterStore();
if (names != null)
for (String name : names)
{
Parameter<?> parameter = store.get(name, new DefaultParameter(name));
if (parameter instanceof ConfigurableParameter<?>)
((ConfigurableParameter<?>) parameter).bindsTo(Evaluation.property(name));
}
parameterized.setParameterStore(store);
}
};
Visitor<Condition> conditionVisitor = new ParameterizedConditionVisitor(callback);
new ConditionVisit(rule).accept(conditionVisitor);
Visitor<Operation> operationVisitor = new ParameterizedOperationVisitor(callback);
new OperationVisit(rule).accept(operationVisitor);
}
}
}
ConfigurationBuilder result = ConfigurationBuilder.begin();
for (Rule rule : allRules)
{
result.addRule(rule);
}
registry.setConfiguration(result);
return registry;
}
private String generatedRuleID(RuleProvider provider, Rule rule, int idx)
{
String provID = provider.getMetadata().getID();
return provID + "_" + idx;
}
}