io.cloudslang.lang.compiler.modeller.transformers.DoTransformer.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.lang.compiler.modeller.transformers.DoTransformer.java

Source

/*******************************************************************************
 * (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.transformers;

import io.cloudslang.lang.compiler.CompilerConstants;
import io.cloudslang.lang.compiler.SlangTextualKeys;
import io.cloudslang.lang.compiler.modeller.result.BasicTransformModellingResult;
import io.cloudslang.lang.compiler.modeller.result.TransformModellingResult;
import io.cloudslang.lang.compiler.validator.ExecutableValidator;
import io.cloudslang.lang.compiler.validator.PreCompileValidator;
import io.cloudslang.lang.entities.SensitivityLevel;
import io.cloudslang.lang.entities.bindings.Argument;
import io.cloudslang.lang.entities.bindings.InOutParam;
import io.cloudslang.lang.entities.bindings.values.ValueFactory;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static io.cloudslang.lang.compiler.SlangTextualKeys.SENSITIVE_KEY;
import static io.cloudslang.lang.compiler.SlangTextualKeys.VALUE_KEY;
import static java.lang.String.format;

/*
 * Created by orius123 on 05/11/14.
 */
public class DoTransformer extends InOutTransformer implements Transformer<Map<String, Object>, List<Argument>> {

    private PreCompileValidator preCompileValidator;
    private ExecutableValidator executableValidator;

    @Override
    public TransformModellingResult<List<Argument>> transform(Map<String, Object> rawData) {
        return transform(rawData, CompilerConstants.DEFAULT_SENSITIVITY_LEVEL);
    }

    @Override
    public TransformModellingResult<List<Argument>> transform(Map<String, Object> rawData,
            SensitivityLevel sensitivityLevel) {
        final List<RuntimeException> validateRawData = validateRawData(rawData);
        if (!validateRawData.isEmpty()) {
            return new BasicTransformModellingResult<>(null, validateRawData);
        }

        final Object rawArguments = rawData.entrySet().iterator().next().getValue();
        if (rawArguments == null) {
            return new BasicTransformModellingResult<>(Collections.emptyList(), Collections.emptyList());
        }

        if (rawArguments instanceof List) {
            final List rawArgumentsList = (List) rawArguments;

            final List<Argument> transformedData = new ArrayList<>();
            final List<RuntimeException> errors = new ArrayList<>();
            // list syntax
            for (final Object rawArgument : rawArgumentsList) {
                try {
                    final Argument argument = transformListArgument(rawArgument, sensitivityLevel);
                    final List<RuntimeException> validationErrors = preCompileValidator
                            .validateNoDuplicateInOutParams(transformedData, argument);
                    if (CollectionUtils.isEmpty(validationErrors)) {
                        transformedData.add(argument);
                    } else {
                        errors.addAll(validationErrors);
                    }
                } catch (RuntimeException rex) {
                    errors.add(rex);
                }
            }
            return new BasicTransformModellingResult<>(transformedData, errors);
        }
        return new BasicTransformModellingResult<>(Collections.emptyList(), Collections.singletonList(
                new RuntimeException("Step arguments should be defined using a standard YAML list.")));
    }

    @Override
    public List<Scope> getScopes() {
        return Collections.singletonList(Scope.BEFORE_STEP);
    }

    @Override
    public String keyToTransform() {
        return SlangTextualKeys.DO_KEY;
    }

    @Override
    public Class<? extends InOutParam> getTransformedObjectsClass() {
        return Argument.class;
    }

    @Override
    public Type getType() {
        return Type.INTERNAL;
    }

    private Argument transformListArgument(Object rawArgument, SensitivityLevel sensitivityLevel) {
        // - some_arg
        // this is our default behaviour that if the user specifies only a key, the key is also the ref we look for
        if (rawArgument instanceof String) {
            String argumentName = (String) rawArgument;
            return createArgument(argumentName, null, false, false, sensitivityLevel);
        } else if (rawArgument instanceof Map) {
            // not so nice casts here - validation happens later on
            // noinspection unchecked
            Map<String, Serializable> rawDataMap = (Map<String, Serializable>) rawArgument;
            Iterator<Map.Entry<String, Serializable>> iterator = rawDataMap.entrySet().iterator();
            Map.Entry<String, ?> entry = iterator.next();
            String entryKey = entry.getKey();
            if (rawDataMap.size() > 1) {
                throw new RuntimeException("Invalid syntax after step input \"" + entryKey + "\". "
                        + "Please check all step inputs are provided as a list and each input"
                        + " is preceded by a hyphen. Step input \"" + iterator.next().getKey() + "\" is"
                        + " missing the hyphen.");
            }
            Serializable entryValue = (Serializable) entry.getValue();
            if (entryValue instanceof Map) {
                // - some_inputs:
                //     property1: value1
                //     property2: value2
                // this is the verbose way of defining inputs with all of the properties available
                // noinspection unchecked
                return createArgumentWithProperties((Map.Entry<String, Map<String, Serializable>>) entry,
                        sensitivityLevel);
            } else {
                // - some_input: some_expression
                return createArgument(entryKey, entryValue, sensitivityLevel);
            }
        }
        throw new RuntimeException("Could not transform step argument: " + rawArgument);
    }

    private Argument createArgumentWithProperties(Map.Entry<String, Map<String, Serializable>> entry,
            SensitivityLevel sensitivityLevel) {
        Map<String, Serializable> props = entry.getValue();
        List<String> knownKeys = Arrays.asList(SENSITIVE_KEY, VALUE_KEY);

        String entryKey = entry.getKey();

        for (String key : props.keySet()) {
            if (!knownKeys.contains(key)) {
                throw new RuntimeException(
                        format("key: %s in step input: %s is not a known property", key, entry.getKey()));
            }
        }

        // default is sensitive=false
        boolean sensitive = props.containsKey(SENSITIVE_KEY) && (boolean) props.get(SENSITIVE_KEY);
        boolean valueKeyFound = props.containsKey(VALUE_KEY);
        Serializable value = valueKeyFound ? props.get(VALUE_KEY) : null;

        return createArgument(entryKey, value, sensitive, valueKeyFound, sensitivityLevel);
    }

    private Argument createArgument(String entryName, Serializable entryValue, SensitivityLevel sensitivityLevel) {
        return createArgument(entryName, entryValue, false, true, sensitivityLevel);
    }

    private Argument createArgument(String entryName, Serializable entryValue, boolean sensitive,
            boolean privateArgument, SensitivityLevel sensitivityLevel) {
        executableValidator.validateInputName(entryName);
        preCompileValidator.validateStringValue(entryName, entryValue, this);
        Accumulator accumulator = extractFunctionData(entryValue);
        return new Argument(entryName, ValueFactory.create(entryValue, sensitive, sensitivityLevel),
                privateArgument, accumulator.getFunctionDependencies(),
                accumulator.getSystemPropertyDependencies());
    }

    public void setPreCompileValidator(PreCompileValidator preCompileValidator) {
        this.preCompileValidator = preCompileValidator;
    }

    public void setExecutableValidator(ExecutableValidator executableValidator) {
        this.executableValidator = executableValidator;
    }

    private List<RuntimeException> validateRawData(Map<String, Object> rawData) {
        if (MapUtils.isEmpty(rawData)) {
            return Collections.singletonList(new RuntimeException("Step has no reference information."));
        } else if (rawData.size() > 1) {
            return Collections.singletonList(new RuntimeException("Step has too many keys under the '"
                    + keyToTransform() + "' keyword,\n" + "May happen due to wrong indentation."));
        }
        executableValidator.validateStepReferenceId(rawData.keySet().iterator().next());
        return Collections.emptyList();
    }
}