/*
* 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.command.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.kie.workbench.common.stunner.core.command.Command;
import org.kie.workbench.common.stunner.core.command.CommandResult;
import org.kie.workbench.common.stunner.core.command.CompositeCommand;
import org.kie.workbench.common.stunner.core.command.util.CommandUtils;
import org.kie.workbench.common.stunner.core.graph.command.GraphCommandExecutionContext;
import org.kie.workbench.common.stunner.core.rule.RuleEvaluationContext;
import org.kie.workbench.common.stunner.core.rule.RuleSet;
import org.kie.workbench.common.stunner.core.rule.RuleViolation;
public abstract class AbstractCompositeCommand<T, V> implements CompositeCommand<T, V> {
private static Logger LOGGER = Logger.getLogger(AbstractCompositeCommand.class.getName());
protected final List<Command<T, V>> commands = new LinkedList<>();
private boolean initialized = false;
public AbstractCompositeCommand<T, V> addCommand(final Command<T, V> command) {
commands.add(command);
return this;
}
protected abstract CommandResult<V> doAllow(final T context,
final Command<T, V> command);
protected abstract CommandResult<V> doExecute(final T context,
final Command<T, V> command);
protected abstract CommandResult<V> doUndo(final T context,
final Command<T, V> command);
@Override
public CommandResult<V> allow(final T context) {
ensureInitialized(context);
final List<CommandResult<V>> results = new LinkedList<>();
for (final Command<T, V> command : commands) {
final CommandResult<V> result = doAllow(context,
command);
results.add(result);
if (CommandUtils.isError(result)) {
break;
}
}
return buildResult(results);
}
@Override
public CommandResult<V> execute(final T context) {
final CommandResult<V> allowResult = this.allow(context);
if (!CommandUtils.isError(allowResult)) {
final Stack<Command<T, V>> executedCommands = new Stack<>();
final List<CommandResult<V>> results = new LinkedList<>();
for (final Command<T, V> command : commands) {
LOGGER.log(Level.FINE,
"Checking execution for command [" + command + "]");
final CommandResult<V> violations = doExecute(context,
command);
LOGGER.log(Level.FINE,
"Execution of command [" + command + "] finished - Violations [" + violations + "]");
results.add(violations);
if (CommandResult.Type.ERROR.equals(violations.getType())) {
undoMultipleExecutedCommands(context,
executedCommands);
break;
}
}
return buildResult(results);
}
return allowResult;
}
protected CommandResult<V> undo(final T context,
final boolean reverse) {
final List<CommandResult<V>> results = new LinkedList<>();
final List<Command<T, V>> collected = reverse ?
commands.stream().collect(reverse()) : commands.stream().collect(forward());
collected.forEach(command -> {
LOGGER.log(Level.FINE,
"Undoing command [" + command + "]");
final CommandResult<V> violations = doUndo(context,
command);
LOGGER.log(Level.FINE,
"Undo of command [" + command + "] finished - Violations [" + violations + "]");
results.add(violations);
});
return buildResult(results);
}
protected AbstractCompositeCommand<T, V> initialize(final T context) {
// Nothing to do by default. Implementation can add commands here.
return this;
}
@Override
public int size() {
return commands.size();
}
public List<Command<T, V>> getCommands() {
return commands;
}
@SuppressWarnings("unchecked")
protected Collection<RuleViolation> doEvaluate(final GraphCommandExecutionContext context,
final RuleEvaluationContext ruleEvaluationContext) {
final RuleSet ruleSet = context.getRuleSet();
return (Collection<RuleViolation>) context.getRuleManager().evaluate(ruleSet,
ruleEvaluationContext)
.violations();
}
protected boolean isInitialized() {
return initialized;
}
protected void ensureInitialized(final T context) {
if (!isInitialized()) {
initialize(context);
initialized = true;
}
}
private CommandResult<V> buildResult(final List<CommandResult<V>> results) {
final CommandResult.Type[] type = {CommandResult.Type.INFO};
final List<V> violations = new LinkedList<>();
results.stream().forEach(rr -> {
if (hasMoreSeverity(rr.getType(),
type[0])) {
type[0] = rr.getType();
}
final Iterable<V> rrIter = rr.getViolations();
if (null != rrIter) {
rrIter.forEach(violations::add);
}
});
return new CommandResultImpl<>(type[0],
violations);
}
private boolean hasMoreSeverity(final CommandResult.Type type,
final CommandResult.Type reference) {
return type.getSeverity() > reference.getSeverity();
}
private CommandResult<V> undoMultipleExecutedCommands(final T context,
final List<Command<T, V>> commandStack) {
final List<CommandResult<V>> results = new LinkedList<>();
commandStack.stream().forEach(command -> results.add(doUndo(context,
command)));
return buildResult(results);
}
private static <T> Collector<T, ?, List<T>> forward() {
return Collectors.toList();
}
private static <T> Collector<T, ?, List<T>> reverse() {
return Collectors.collectingAndThen(Collectors.toList(),
l -> {
Collections.reverse(l);
return l;
});
}
@Override
public String toString() {
String s = "[" + getClass().getName() + "]";
for (int x = 0; x < commands.size(); x++) {
s += " {(" + x + ") [" + commands.get(x) + "]} ";
}
return s;
}
}