org.openscore.lang.tools.verifier.SlangContentVerifier.java Source code

Java tutorial

Introduction

Here is the source code for org.openscore.lang.tools.verifier.SlangContentVerifier.java

Source

/*
 * (c) Copyright 2014 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 org.openscore.lang.tools.verifier;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.openscore.lang.compiler.SlangCompiler;
import org.openscore.lang.compiler.modeller.model.Executable;
import org.openscore.lang.compiler.scorecompiler.ScoreCompiler;
import org.openscore.lang.entities.CompilationArtifact;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.openscore.lang.compiler.SlangSource.fromFile;

/*
 * Created by stoneo on 2/9/2015.
 */
/**
 * Verifies all files with extensions: .sl, .sl.yaml or .sl.yml in a given directory are valid
 */
public class SlangContentVerifier {

    @Autowired
    private SlangCompiler slangCompiler;

    @Autowired
    private ScoreCompiler scoreCompiler;

    private final static Logger log = Logger.getLogger(VerifierMain.class);

    private String[] SLANG_FILE_EXTENSIONS = { "sl", "sl.yaml", "sl.yml" };

    /**
     * Transform all Slang files in given directory to Slang models, and store them
     * @param directoryPath given directory containing all Slang files
     * @return the number of valid Slang files in the given directory
     */
    public int verifyAllSlangFilesInDirAreValid(String directoryPath) {
        Map<String, Executable> slangModels = transformSlangFilesInDirToModels(directoryPath);
        return compileAllSlangModels(slangModels);
    }

    private Map<String, Executable> transformSlangFilesInDirToModels(String directoryPath) {
        Validate.notEmpty(directoryPath, "You must specify a path");
        Validate.isTrue(new File(directoryPath).isDirectory(),
                "Directory path argument \'" + directoryPath + "\' does not lead to a directory");
        Map<String, Executable> slangModels = new HashMap<>();
        Collection<File> slangFiles = FileUtils.listFiles(new File(directoryPath), SLANG_FILE_EXTENSIONS, true);
        log.info("Start compiling all slang files under: " + directoryPath);
        log.info(slangFiles.size() + " .sl files were found");
        for (File slangFile : slangFiles) {
            Validate.isTrue(slangFile.isFile(),
                    "file path \'" + slangFile.getAbsolutePath() + "\' must lead to a file");
            Executable sourceModel;
            try {
                sourceModel = slangCompiler.preCompile(fromFile(slangFile));
            } catch (Exception e) {
                String errorMessage = "Failed creating Slang models for file: \'" + slangFile.getAbsoluteFile()
                        + "\'.\n" + e.getMessage();
                log.error(errorMessage);
                throw new RuntimeException(errorMessage, e);
            }
            if (sourceModel != null) {
                staticSlangFileValidation(slangFile, sourceModel);
                slangModels.put(getUniqueName(sourceModel), sourceModel);
            }
        }
        if (slangFiles.size() != slangModels.size()) {
            throw new RuntimeException("Some Slang files were not pre-compiled.\nFound: " + slangFiles.size()
                    + " slang files in path: \'" + directoryPath
                    + "\' But managed to create slang models for only: " + slangModels.size());
        }
        return slangModels;
    }

    private int compileAllSlangModels(Map<String, Executable> slangModels) {
        Collection<Executable> models = slangModels.values();
        Map<String, CompilationArtifact> compiledArtifacts = new HashMap<>();
        for (Executable slangModel : models) {
            try {
                Set<Executable> dependenciesModels = getModelDependenciesRecursively(slangModels, slangModel);
                CompilationArtifact compiledSource = compiledArtifacts.get(slangModel.getName());
                if (compiledSource == null) {
                    compiledSource = scoreCompiler.compile(slangModel, dependenciesModels);
                    if (compiledSource != null) {
                        log.info("Compiled: \'" + slangModel.getNamespace() + "." + slangModel.getName()
                                + "\' successfully");
                        compiledArtifacts.put(slangModel.getName(), compiledSource);
                    } else {
                        log.error("Failed to compile source: \'" + slangModel.getNamespace() + "."
                                + slangModel.getName() + "\'");
                    }
                }
            } catch (Exception e) {
                String errorMessage = "Failed compiling Slang source: \'" + slangModel.getNamespace() + "."
                        + slangModel.getName() + "\'.\n" + e.getMessage();
                log.error(errorMessage);
                throw new RuntimeException(errorMessage, e);
            }
        }
        if (compiledArtifacts.size() != slangModels.size()) {
            throw new RuntimeException("Some Slang files were not compiled.\n" + "Found: " + slangModels.size()
                    + " slang models, but managed to compile only: " + compiledArtifacts.size());
        }
        String successMessage = "Successfully finished Compilation of: " + compiledArtifacts.size()
                + " Slang files";
        log.info(successMessage);
        return compiledArtifacts.size();
    }

    private Set<Executable> getModelDependenciesRecursively(Map<String, Executable> slangModels,
            Executable slangModel) {
        Set<Executable> dependenciesModels = new HashSet<>();
        for (String dependencyName : slangModel.getDependencies()) {
            Executable dependency = slangModels.get(dependencyName);
            if (dependency == null) {
                throw new RuntimeException("Failed compiling slang source: " + slangModel.getNamespace() + "."
                        + slangModel.getName() + ". Missing dependency: " + dependencyName);
            }
            dependenciesModels.add(dependency);
            dependenciesModels.addAll(getModelDependenciesRecursively(slangModels, dependency));
        }
        return dependenciesModels;
    }

    private void staticSlangFileValidation(File slangFile, Executable executable) {
        validateNamespace(slangFile, executable);

        validateExecutableName(slangFile, executable);
    }

    private void validateExecutableName(File slangFile, Executable executable) {
        // Validate executable name is the same as the file name
        String[] splitName = slangFile.getName().split("\\Q.");
        String fileNameNoExtension = splitName[0];
        String executableNameErrorMessage = "Error validating Slang file: \'" + slangFile.getAbsoluteFile()
                + "\'. Name of flow or operation: \'" + executable.getName()
                + "\' is invalid.\nIt should be identical to the file name: \'" + fileNameNoExtension + "\'";
        Validate.isTrue(fileNameNoExtension.equals(executable.getName()), executableNameErrorMessage);
    }

    private void validateNamespace(File slangFile, Executable executable) {
        // Validate that the namespace is not empty
        String namespace = executable.getNamespace();
        Validate.notEmpty(namespace, "Error validating Slang file: \'" + slangFile.getAbsoluteFile()
                + "\'. Namespace of slang source: \'" + executable.getName() + "\' cannot be empty.");

        // Validate that the namespace matches the path of the file
        String executableNamespacePath = namespace.replace('.', File.separatorChar);
        String namespaceErrorMessage = "Error validating Slang file: \'" + slangFile.getAbsoluteFile()
                + "\'. Namespace of slang source: " + executable.getName() + " is wrong.\nIt is currently \'"
                + namespace + "\', but it should match the file path: \'" + slangFile.getPath() + "\'";
        int indexOfLastFileSeparator = slangFile.getAbsolutePath().lastIndexOf(File.separatorChar);
        String filePathWithoutFileName = slangFile.getAbsolutePath().substring(0, indexOfLastFileSeparator);
        Validate.isTrue(filePathWithoutFileName.toLowerCase().endsWith(executableNamespacePath.toLowerCase()),
                namespaceErrorMessage);

        // Validate that the namespace is composed only of abc letters, _ or -
        Pattern pattern = Pattern.compile("^[\\w-\\.]+$");
        Matcher matcher = pattern.matcher(namespace);
        Validate.isTrue(matcher.matches(), "Namespace: " + namespace
                + " is invalid. It can contain only alphanumeric characters, underscore or hyphen");
    }

    private String getUniqueName(Executable sourceModel) {
        return sourceModel.getNamespace() + "." + sourceModel.getName();
    }

}