/*******************************************************************************
* (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;
import io.cloudslang.lang.compiler.modeller.model.Metadata;
import io.cloudslang.lang.compiler.modeller.model.StepMetadata;
import io.cloudslang.lang.compiler.modeller.result.MetadataModellingResult;
import io.cloudslang.lang.compiler.parser.model.ParsedDescriptionData;
import io.cloudslang.lang.compiler.parser.model.ParsedDescriptionSection;
import io.cloudslang.lang.compiler.parser.utils.DescriptionTag;
import io.cloudslang.lang.compiler.parser.utils.StepDescriptionTag;
import io.cloudslang.lang.compiler.validator.matcher.DescriptionPatternMatcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
public class MetadataModellerImpl implements MetadataModeller {
private DescriptionPatternMatcher descriptionPatternMatcher;
public MetadataModellerImpl() {
descriptionPatternMatcher = new DescriptionPatternMatcher();
}
@Override
public MetadataModellingResult createModel(ParsedDescriptionData parsedDescriptionData) {
List<ParsedDescriptionSection> topLevelDescriptions = parsedDescriptionData.getTopLevelDescriptions();
List<RuntimeException> errors = new ArrayList<>(parsedDescriptionData.getErrors());
Pair<Metadata, List<RuntimeException>> executableMetadata = null;
// executable metadata
if (CollectionUtils.isNotEmpty(topLevelDescriptions)) {
if (topLevelDescriptions.size() > 1) {
errors.add(
new RuntimeException(
"Multiple top level descriptions found at line numbers: " +
getLineNumbers(topLevelDescriptions)
)
);
}
executableMetadata =
transformToExecutableMetadata(topLevelDescriptions.get(0).getData());
errors.addAll(executableMetadata.getRight());
}
// step metadata
Pair<List<StepMetadata>, List<RuntimeException>> stepsModellingResult =
transformStepsData(parsedDescriptionData.getStepDescriptions());
List<StepMetadata> stepDescriptions = stepsModellingResult.getLeft();
errors.addAll(stepsModellingResult.getRight());
return new MetadataModellingResult(
executableMetadata == null ? new Metadata() : executableMetadata.getLeft(),
stepDescriptions,
errors
);
}
private String getLineNumbers(List<ParsedDescriptionSection> topLevelDescriptions) {
int[] lineNumbers = new int[topLevelDescriptions.size()];
for (int i = 0; i < topLevelDescriptions.size(); i++) {
lineNumbers[i] = topLevelDescriptions.get(i).getStartLineNumber();
}
return Arrays.toString(lineNumbers);
}
private Pair<Metadata, List<RuntimeException>> transformToExecutableMetadata(
Map<String, String> parsedData) {
String description = "";
String prerequisites = "";
Map<String, String> inputs = new LinkedHashMap<>();
Map<String, String> outputs = new LinkedHashMap<>();
Map<String, String> results = new LinkedHashMap<>();
List<RuntimeException> errors = new ArrayList<>();
for (Map.Entry<String, String> entry : parsedData.entrySet()) {
String declaration = entry.getKey();
String[] declarationElements = descriptionPatternMatcher.splitDeclaration(declaration);
String tag = declarationElements[0];
if (DescriptionTag.isDescriptionTag(tag)) {
String content = entry.getValue();
DescriptionTag descriptionTag = DescriptionTag.fromString(tag);
if (descriptionTag != null) {
switch (descriptionTag) {
case DESCRIPTION:
description = content;
break;
case PREREQUISITES:
prerequisites = content;
break;
case INPUT:
processExecutableDeclaration(declarationElements, errors, tag, inputs, content);
break;
case OUTPUT:
processExecutableDeclaration(declarationElements, errors, tag, outputs, content);
break;
case RESULT:
processExecutableDeclaration(declarationElements, errors, tag, results, content);
break;
default:
// shouldn't get here
errors.add(new NotImplementedException("Unrecognized tag: " + descriptionTag));
}
} else {
// shouldn't get here
errors.add(new RuntimeException("Unrecognized tag: " + tag));
}
} else {
errors.add(new RuntimeException("Unrecognized tag for executable description section: " + tag));
}
}
Metadata executableMetadata = new Metadata(
description,
prerequisites,
inputs,
outputs,
results
);
return new ImmutablePair<>(executableMetadata, errors);
}
private void processExecutableDeclaration(
String[] declarationElements,
List<RuntimeException> errors,
String tag,
Map<String, String> parameters,
String content) {
processDeclaration(declarationElements, errors, tag, parameters, content, "executable");
}
private void processStepDeclaration(
String[] declarationElements,
List<RuntimeException> errors,
String tag,
Map<String, String> parameters,
String content,
String stepName) {
processDeclaration(declarationElements, errors, tag, parameters, content, "step[" + stepName + "]");
}
private void processDeclaration(
String[] declarationElements,
List<RuntimeException> errors,
String tag,
Map<String, String> parameters,
String content,
String identifier) {
if (declarationElements.length != 2) {
errors.add(
new RuntimeException(
"For " + identifier + " parameter name for tag[" + tag + "] is missing. " +
"Format should be [" + tag + " name]"
)
);
} else {
String name = declarationElements[1];
parameters.put(name, content);
}
}
private Pair<List<StepMetadata>, List<RuntimeException>> transformStepsData(
Map<String, ParsedDescriptionSection> stepsData) {
List<StepMetadata> stepsMetadata = new ArrayList<>();
List<RuntimeException> errors = new ArrayList<>();
for (Map.Entry<String, ParsedDescriptionSection> entry : stepsData.entrySet()) {
String stepName = entry.getKey();
ParsedDescriptionSection parsedData = entry.getValue();
Pair<StepMetadata, List<RuntimeException>> transformResult =
transformToStepMetadata(stepName, parsedData.getData());
stepsMetadata.add(transformResult.getLeft());
errors.addAll(transformResult.getRight());
}
return new ImmutablePair<>(stepsMetadata, errors);
}
private Pair<StepMetadata, List<RuntimeException>> transformToStepMetadata(
String stepName,
Map<String, String> parsedData) {
Map<String, String> inputs = new LinkedHashMap<>();
Map<String, String> outputs = new LinkedHashMap<>();
List<RuntimeException> errors = new ArrayList<>();
for (Map.Entry<String, String> entry : parsedData.entrySet()) {
String declaration = entry.getKey();
String[] declarationElements = descriptionPatternMatcher.splitDeclaration(declaration);
String tag = declarationElements[0];
if (StepDescriptionTag.isStepDescriptionTag(tag)) {
String content = entry.getValue();
StepDescriptionTag descriptionTag = StepDescriptionTag.fromString(tag);
if (descriptionTag != null) {
switch (descriptionTag) {
case INPUT:
processStepDeclaration(declarationElements, errors, tag, inputs, content, stepName);
break;
case OUTPUT:
processStepDeclaration(declarationElements, errors, tag, outputs, content, stepName);
break;
default:
// shouldn't get here
errors.add(new NotImplementedException("Unrecognized tag: " + descriptionTag));
}
} else {
// shouldn't get here
errors.add(new RuntimeException("Unrecognized tag: " + tag));
}
} else {
errors.add(new RuntimeException("Unrecognized tag for step description section: " + tag));
}
}
StepMetadata stepMetadata = new StepMetadata(
stepName,
inputs,
outputs
);
return new ImmutablePair<>(stepMetadata, errors);
}
}