/*******************************************************************************
* (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License v2.0 which accompany this distribution.
*
* The Apache License is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
*******************************************************************************/
package io.cloudslang.lang.compiler.modeller;
/*
* Created by orius123 on 05/11/14.
*/
import ch.lambdaj.Lambda;
import io.cloudslang.lang.compiler.SlangTextualKeys;
import io.cloudslang.lang.compiler.modeller.model.Executable;
import io.cloudslang.lang.compiler.modeller.model.Flow;
import io.cloudslang.lang.compiler.modeller.model.Step;
import io.cloudslang.lang.compiler.modeller.transformers.PublishTransformer;
import io.cloudslang.lang.compiler.modeller.transformers.Transformer;
import io.cloudslang.lang.entities.LoopStatement;
import io.cloudslang.lang.entities.bindings.InOutParam;
import io.cloudslang.lang.entities.bindings.Input;
import io.cloudslang.lang.entities.bindings.Output;
import io.cloudslang.lang.entities.bindings.Result;
import io.cloudslang.lang.entities.constants.Messages;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.Validate;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static ch.lambdaj.Lambda.selectFirst;
import static org.hamcrest.Matchers.equalTo;
public class DependenciesHelper {
private PublishTransformer publishTransformer;
public Set<String> fetchDependencies(Executable executable, Map<String, Executable> availableDependencies) {
Validate.notNull(executable);
Validate.notNull(availableDependencies);
switch (executable.getType()) {
case SlangTextualKeys.OPERATION_TYPE:
return new HashSet<>();
case SlangTextualKeys.DECISION_TYPE:
return new HashSet<>();
case SlangTextualKeys.FLOW_TYPE:
return processFlowForDependencies((Flow) executable, availableDependencies);
default:
throw new NotImplementedException(Messages.UNKNOWN_EXECUTABLE_TYPE);
}
}
private Set<String> processFlowForDependencies(Flow flow, Map<String, Executable> availableDependencies) {
Set<String> flowDependencies = new HashSet<>();
for (Step step : flow.getWorkflow().getSteps()) {
String stepReferenceId = step.getRefId();
Executable stepReference = availableDependencies.get(stepReferenceId);
flowDependencies.add(stepReferenceId);
flowDependencies.addAll(fetchDependencies(stepReference, availableDependencies));
}
return flowDependencies;
}
/**
* recursive matches executables with their references
*
* @param availableDependencies the executables to match from
* @return a map of a the executables that were successfully matched
*/
public Map<String, Executable> matchReferences(Executable executable,
Collection<Executable> availableDependencies) {
Validate.isTrue(executable.getType().equals(SlangTextualKeys.FLOW_TYPE),
"Executable: \'" + executable.getId() + "\' is not a flow, therefore it has no references");
Map<String, Executable> resolvedDependencies = new HashMap<>();
return fetchFlowReferences(executable, availableDependencies, resolvedDependencies);
}
private Map<String, Executable> fetchFlowReferences(Executable executable,
Collection<Executable> availableDependencies,
Map<String, Executable> resolvedDependencies) {
for (String refId : executable.getExecutableDependencies()) {
//if it is already in the references we do nothing
if (resolvedDependencies.get(refId) == null) {
Executable matchingRef = selectFirst(availableDependencies,
having(on(Executable.class).getId(), equalTo(refId)));
if (matchingRef == null) {
throw new RuntimeException("Reference: \'" + refId + "\' in executable: \'" +
executable.getName() + "\', wasn't found in path");
}
//first we put the reference on the map
resolvedDependencies.put(matchingRef.getId(), matchingRef);
if (matchingRef.getType().equals(SlangTextualKeys.FLOW_TYPE)) {
//if it is a flow we recursively
resolvedDependencies
.putAll(fetchFlowReferences(matchingRef, availableDependencies, resolvedDependencies));
}
}
}
return resolvedDependencies;
}
public Set<String> getSystemPropertiesForFlow(
List<Input> inputs,
List<Output> outputs,
List<Result> results,
Deque<Step> steps) {
Set<String> result = new HashSet<>();
result.addAll(getSystemPropertiesFromExecutable(inputs, outputs, results));
for (Step step : steps) {
result.addAll(getSystemPropertiesFromStep(step));
}
return result;
}
public Set<String> getSystemPropertiesForOperation(
List<Input> inputs,
List<Output> outputs,
List<Result> results) {
return getSystemPropertiesFromExecutable(inputs, outputs, results);
}
public Set<String> getSystemPropertiesForDecision(
List<Input> inputs,
List<Output> outputs,
List<Result> results) {
return getSystemPropertiesFromExecutable(inputs, outputs, results);
}
private Set<String> getSystemPropertiesFromExecutable(
List<Input> inputs,
List<Output> outputs,
List<Result> results) {
Set<String> result = new HashSet<>();
result.addAll(getSystemPropertiesFromInOutParam(inputs));
result.addAll(getSystemPropertiesFromInOutParam(outputs));
result.addAll(getSystemPropertiesFromInOutParam(results));
return result;
}
private Set<String> getSystemPropertiesFromStep(Step step) {
Set<String> result = new HashSet<>();
List<Transformer> relevantTransformers = new ArrayList<>();
relevantTransformers.add(publishTransformer);
result.addAll(getSystemPropertiesFromLoopStatement(step.getPreStepActionData()));
result.addAll(getSystemPropertiesFromInOutParam(step.getArguments()));
result.addAll(
getSystemPropertiesFromPostStepActionData(
step.getPostStepActionData(),
relevantTransformers,
step.getName()
)
);
return result;
}
private Set<String> getSystemPropertiesFromLoopStatement(Map<String, Serializable> preStepActionData) {
Set<String> result = new HashSet<>();
for (Map.Entry<String, Serializable> entry : preStepActionData.entrySet()) {
if (entry.getValue() instanceof LoopStatement) {
result.addAll(((LoopStatement) entry.getValue()).getSystemPropertyDependencies());
}
}
return result;
}
private Set<String> getSystemPropertiesFromInOutParam(List<? extends InOutParam> inOutParams) {
Set<String> result = new HashSet<>();
if (inOutParams != null) {
for (InOutParam inOutParam : inOutParams) {
Set<String> systemPropertyDependencies = inOutParam.getSystemPropertyDependencies();
if (CollectionUtils.isNotEmpty(systemPropertyDependencies)) {
result.addAll(systemPropertyDependencies);
}
}
}
return result;
}
private Set<String> getSystemPropertiesFromPostStepActionData(
Map<String, Serializable> postStepActionData,
List<Transformer> relevantTransformers,
String stepName) {
Set<String> result = new HashSet<>();
for (Transformer transformer : relevantTransformers) {
String key = TransformersHandler.keyToTransform(transformer);
Serializable item = postStepActionData.get(key);
if (item instanceof Collection) {
Collection itemsCollection = (Collection) item;
for (Object itemAsObject : itemsCollection) {
if (itemAsObject instanceof Output) {
Output itemAsOutput = (Output) itemAsObject;
result.addAll(itemAsOutput.getSystemPropertyDependencies());
} else {
throw new RuntimeException("For step: " + stepName +
" - Incorrect type for post step data items.");
}
}
} else {
throw new RuntimeException("For step: " + stepName + " - Incorrect type for post step data items.");
}
}
return result;
}
public void setPublishTransformer(PublishTransformer publishTransformer) {
this.publishTransformer = publishTransformer;
}
}