/* * Copyright 2017 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.workbench.common.stunner.core.rule; import java.util.Collection; import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import org.kie.workbench.common.stunner.core.registry.RegistryFactory; import org.kie.workbench.common.stunner.core.registry.rule.RuleHandlerRegistry; import org.kie.workbench.common.stunner.core.rule.ext.RuleExtension; import org.kie.workbench.common.stunner.core.rule.ext.RuleExtensionHandler; import org.kie.workbench.common.stunner.core.rule.violations.ContextOperationNotAllowedViolation; import org.kie.workbench.common.stunner.core.rule.violations.DefaultRuleViolations; import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull; @ApplicationScoped public class RuleManagerImpl implements RuleManager { private static Logger LOGGER = Logger.getLogger(RuleManagerImpl.class.getName()); private final RuleHandlerRegistry registry; protected RuleManagerImpl() { this(null); } @Inject public RuleManagerImpl(final RegistryFactory registryFactory) { this.registry = null != registryFactory ? registryFactory.newRuleHandlerRegistry() : null; } @Override public RuleViolations evaluate(final RuleSet ruleSet, final RuleEvaluationContext context) { checkNotNull("ruleSet", ruleSet); checkNotNull("context", context); /* Consider: - If no rules present on the rule set, no resulting rule violation instances are expected - If rules present but no rule accepts the runtime context inputs, the context type defines if allow/or deny the evaluation - Otherwise return the rule violations produced by the handlers or extensions */ final DefaultRuleViolations results = new DefaultRuleViolations(); final boolean hasRules = ruleSet.getRules().iterator().hasNext(); if (hasRules) { final boolean[] hasEvaluations = {false}; ruleSet.getRules().forEach(rule -> { final Optional<RuleViolations> violations = evaluate(rule, context); if (violations.isPresent()) { hasEvaluations[0] = true; LOGGER.info("Rule Evaluation [" + rule + ", " + violations + "]"); results.addViolations(violations.get()); } }); if (!hasEvaluations[0] && context.isDefaultDeny()) { return getDefaultViolationForContext(context); } } return results; } private RuleViolations getDefaultViolationForContext(final RuleEvaluationContext context) { return new DefaultRuleViolations().addViolation( new ContextOperationNotAllowedViolation(context) ); } @Override public RuleHandlerRegistry registry() { return registry; } private Optional<RuleViolations> evaluate(final Rule rule, final RuleEvaluationContext context) { if (rule instanceof RuleExtension) { return evaluateExtension((RuleExtension) rule, context); } return evaluateRule(rule, context); } @SuppressWarnings("unchecked") private Optional<RuleViolations> evaluateRule(final Rule rule, final RuleEvaluationContext context) { checkNotNull("rule", rule); checkNotNull("context", context); final Collection<RuleEvaluationHandler> handlers = getHandler(rule, context); final DefaultRuleViolations results = new DefaultRuleViolations(); if (!handlers.isEmpty()) { handlers.forEach(h -> results.addViolations(h.evaluate(rule, context))); return Optional.of(results); } return Optional.empty(); } @SuppressWarnings("unchecked") private Optional<RuleViolations> evaluateExtension(final RuleExtension rule, final RuleEvaluationContext context) { checkNotNull("rule", rule); checkNotNull("context", context); final Optional<RuleExtensionHandler> handler = getExtensionHandler(rule, context); return handler.isPresent() ? Optional.of(handler.get().evaluate(rule, context)) : Optional.empty(); } private Collection<RuleEvaluationHandler> getHandler(final Rule rule, final RuleEvaluationContext context) { final Collection<RuleEvaluationHandler> handlers = registry.getHandlersByContext(context.getType()); return handlers.stream() .filter(h -> accepts(h, rule, context)) .collect(Collectors.toList()); } private Optional<RuleExtensionHandler> getExtensionHandler(final RuleExtension rule, final RuleEvaluationContext context) { final RuleExtensionHandler handler = registry.getExtensionHandler(rule.getHandlerType()); return null != handler && accepts(handler, rule, context) ? Optional.of(handler) : Optional.empty(); } /** * Handler acceptance based on: * 1- Rule and context types - for performance and computing purposes. * 2- Once types are known accepted - do a second acceptance evaluation based * on the context's state at runtime. * 3.- Once 1) AND 2) - the handler is able to perform more complex runtime * evaluation, the evaluation can be delegated to it. */ @SuppressWarnings("unchecked") private boolean accepts(final RuleEvaluationHandler handler, final Rule rule, final RuleEvaluationContext context) { return handler.getRuleType().equals(rule.getClass()) && (handler.getContextType().equals(context.getType()) || RuleEvaluationContext.class.equals(handler.getContextType())) && handler.accepts(rule, context); } }