org.csstudio.opibuilder.validation.Application.java Source code

Java tutorial

Introduction

Here is the source code for org.csstudio.opibuilder.validation.Application.java

Source

/*******************************************************************************
 * Copyright (c) 2010-2016 ITER Organization.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 ******************************************************************************/
package org.csstudio.opibuilder.validation;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.xml.bind.JAXB;

import org.apache.commons.io.FileUtils;
import org.csstudio.opibuilder.validation.ProjectDescription.Link;
import org.csstudio.opibuilder.validation.ProjectDescription.Variable;
import org.csstudio.opibuilder.validation.core.SchemaVerifier;
import org.csstudio.opibuilder.validation.core.ValidationFailure;
import org.csstudio.opibuilder.validation.core.Validator;
import org.eclipse.core.runtime.Path;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;

/**
 *
 * <code>Application</code> verifies the opi files provided by the parameters against the provided schema and validation
 * rules. The results of validation are either printed to the console or into a file specified by one of the parameters.
 *
 * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a>
 *
 */
public class Application implements IApplication {

    private static final char SEPARATOR = ';';
    private static final String HEADER = "PATH;WIDGET_NAME;WIDGET_TYPE;LINE_NUMBER;PROPERTY;VALUE;EXPECTED_VALUE";

    private static final String VALIDATION_RULES = "-rules";
    private static final String SCHEMA = "-schema";
    private static final String OPI_LOCATION = "-opilocation";
    private static final String RESULTS = "-results";
    private static final String HELP = "-help";
    private static final String VERSION = "-version";
    private static final String PRINT_RESULTS = "-print";
    private static final String INCLUDE_TARGET_FOLDER = "-includeTarget";

    private File targetToDelete = null;
    private boolean deleteWhenFinished = false;
    private boolean skipTargetFolder = true;

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
     */
    @Override
    public Object start(IApplicationContext context) throws Exception {
        String rules = null;
        String location = null;
        String schema = null;
        String results = null;
        boolean printResults = false;
        final String args[] = (String[]) context.getArguments().get("application.args");
        for (int i = 0; i < args.length; i++) {
            if (HELP.equals(args[i])) {
                printHelp();
                return EXIT_OK;
            } else if (VERSION.equals(args[i])) {
                String version = (String) context.getBrandingBundle().getHeaders().get("Bundle-Version");
                System.out.println(version);
                return EXIT_OK;
            } else if (PRINT_RESULTS.equals(args[i])) {
                printResults = true;
            } else if (VALIDATION_RULES.equals(args[i])) {
                rules = args[++i];
            } else if (OPI_LOCATION.equals(args[i])) {
                location = args[++i];
            } else if (SCHEMA.equals(args[i])) {
                schema = args[++i];
            } else if (RESULTS.equals(args[i])) {
                results = args[++i];
            } else if (INCLUDE_TARGET_FOLDER.equals(args[i])) {
                skipTargetFolder = false;
            }
        }

        if (rules == null) {
            System.err.println("Validation Rules file is not defined!");
            Thread.sleep(300);
            printHelp();
            Thread.sleep(1000);
            return EXIT_OK;
        } else if (schema == null) {
            System.err.println("OPI Schema is not defined!");
            Thread.sleep(300);
            printHelp();
            Thread.sleep(1000);
            return EXIT_OK;
        } else if (location == null) {
            location = new File(".").getAbsoluteFile().getParentFile().getAbsolutePath();
        }

        File file = new File(location);
        if (!file.exists()) {
            System.err.println("The path '" + location + "' does not exist.");
            Thread.sleep(1000);
            return EXIT_OK;
        }
        Path schemaPath = new Path(new File(schema).getAbsolutePath());
        Path rulesPath = new Path(new File(rules).getAbsolutePath());
        SchemaVerifier verifier = Validator.createVerifier(schemaPath, rulesPath, null);
        System.out.println("Checking for linked resources...");
        file = setUpLocation(file);
        System.out.println("Prepared the validation target: " + file.getAbsolutePath());
        System.out.println("Validation started...");
        check(verifier, file);
        ValidationFailure[] failures = verifier.getValidationFailures();
        if (deleteWhenFinished) {
            try {
                FileUtils.deleteDirectory(targetToDelete);
            } catch (IOException e) {
                System.err.println("WARNING: " + e.getMessage());
            }
        }
        System.out.println("Validation finished.");
        StringBuilder sb = new StringBuilder(1000);
        sb.append("Validated files: ").append(verifier.getNumberOfAnalyzedFiles());
        sb.append("\nFiles with failures: ").append(verifier.getNumberOfFilesFailures());
        sb.append("\nValidated widgets: ").append(verifier.getNumberOfAnalyzedWidgets());
        sb.append("\nWidgets with failures: ").append(verifier.getNumberOfWidgetsFailures());
        sb.append("\nValidated RO properties: ").append(verifier.getNumberOfROProperties());
        sb.append("\nCritical RO failures: ").append(verifier.getNumberOfCriticalROFailures());
        sb.append("\nMajor RO failures: ").append(verifier.getNumberOfMajorROFailures());
        sb.append("\nValidated WRITE properties: ").append(verifier.getNumberOfWRITEProperties());
        sb.append("\nWRITE failures: ").append(verifier.getNumberOfWRITEFailures());
        sb.append("\nValidated RW properties: ").append(verifier.getNumberOfRWProperties());
        sb.append("\nRW failures: ").append(verifier.getNumberOfRWFailures());
        sb.append("\nDeprecated properties used: ").append(verifier.getNumberOfDeprecatedFailures());
        sb.append("\nScripts & Rules usage:");
        sb.append("\n    Jython standalone: ").append(verifier.getNumberOfPythonStandalone()).append(' ')
                .append('(').append(verifier.getNumberOfWidgetsWithPythonStandalone())
                .append(verifier.getNumberOfWidgetsWithPythonStandalone() == 1 ? " widget)" : " widgets)");
        sb.append("\n    Jython embedded: ").append(verifier.getNumberOfPythonEmbedded()).append(' ').append('(')
                .append(verifier.getNumberOfWidgetsWithPythonEmbedded())
                .append(verifier.getNumberOfWidgetsWithPythonEmbedded() == 1 ? " widget)" : " widgets)");
        sb.append("\n    Javascript standalone: ").append(verifier.getNumberOfJavascriptStandalone()).append(' ')
                .append('(').append(verifier.getNumberOfWidgetsWithJavascriptStandalone())
                .append(verifier.getNumberOfWidgetsWithJavascriptStandalone() == 1 ? " widget)" : " widgets)");
        sb.append("\n    Javascript embedded: ").append(verifier.getNumberOfJavascriptEmbedded()).append(' ')
                .append('(').append(verifier.getNumberOfWidgetsWithJavascriptEmbedded())
                .append(verifier.getNumberOfWidgetsWithJavascriptEmbedded() == 1 ? " widget)" : " widgets)");
        sb.append("\n    Rules: ").append(verifier.getNumberOfAllRules()).append(' ').append('(')
                .append(verifier.getNumberOfWidgetsWithRules())
                .append(verifier.getNumberOfWidgetsWithRules() == 1 ? " widget)\n" : " widgets)\n");

        if (printResults) {
            System.out.println(HEADER);
            for (ValidationFailure f : failures) {
                System.out.println(toMessage(f));
            }
            System.out.println(sb.toString());
        }

        if (results != null) {
            System.out.println("Results printed to file '" + new File(results).getAbsolutePath() + "'.");
            try (PrintWriter pw = new PrintWriter(new File(results), StandardCharsets.UTF_8.name())) {
                pw.println(HEADER);
                for (ValidationFailure f : failures) {
                    pw.println(toMessage(f));
                }
            }

            int idx = results.lastIndexOf('.');
            String summaryFile;
            if (idx < 1) {
                summaryFile = results + "_summary";
            } else {
                summaryFile = results.substring(0, idx) + "_summary" + results.substring(idx);
            }
            System.out
                    .println("Results summary printed to file '" + new File(summaryFile).getAbsolutePath() + "'.");
            try (PrintWriter pw = new PrintWriter(new File(summaryFile), StandardCharsets.UTF_8.name())) {
                pw.println(sb.toString());
            }
        }

        return EXIT_OK;
    }

    /**
     * Convert the validation failure into a csv format: file path; widget name; line number; property; message
     *
     * @param f the validation failure
     * @return the full message describing the failure
     */
    private String toMessage(ValidationFailure f) {
        StringBuilder sb = new StringBuilder(300);
        sb.append(f.getPath()).append(SEPARATOR).append(f.getWidgetName()).append(SEPARATOR)
                .append(f.getWidgetType()).append(SEPARATOR).append(f.getLineNumber()).append(SEPARATOR)
                .append(f.getProperty()).append(SEPARATOR).append('"').append(f.getActual()).append('"')
                .append(SEPARATOR).append('"').append(f.getExpected()).append('"');
        return sb.toString();
    }

    /**
     * Checks the given file. If the file is a directory all its children are checked. A file is only checked if it is
     * an opi file.
     *
     * @param verifier the verifier to use for checking
     * @param file the file to check
     * @throws IllegalStateException
     * @throws IOException
     */
    private void check(SchemaVerifier verifier, File file) throws IllegalStateException, IOException {
        if (file.isDirectory()) {
            if (skipTargetFolder && isTargetFolder(file)) {
                return;
            }
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    check(verifier, f);
                }
            }
        } else if (file.getAbsolutePath().toLowerCase(Locale.UK).endsWith(".opi")) {
            System.out.println("Validating file: " + file.getAbsolutePath());
            verifier.validate(new Path(file.getAbsolutePath()));
        }
    }

    private static boolean isTargetFolder(File file) {
        if ("target".equalsIgnoreCase(file.getName())) {
            String[] siblings = file.getParentFile().list();
            if (siblings != null) {
                for (String s : siblings) {
                    if ("src".equalsIgnoreCase(s)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private void printHelp() {
        System.out.println("Options:");
        System.out.println(String.format("  %-30s: %s", HELP, "Print this help."));
        System.out.println(String.format("  %-30s: %s", VERSION, "Print the version number of this tool."));
        System.out.println(String.format("  %-30s: %s", VALIDATION_RULES + " <FILE>",
                "Path to the file with validation rules."));
        System.out.println(String.format("  %-30s: %s", SCHEMA + " <FILE>", "Path to the OPI schema file."));
        System.out.println(String.format("  %-30s: %s", OPI_LOCATION + " <PATH>",
                "Path to the OPI file or folder to validate. If null current directory is used."));
        System.out.println(String.format("  %-30s: %s", RESULTS + " <FILE> ",
                "Path to the file into which the results will be printed"));
        System.out.println(String.format("  %-30s: %s", PRINT_RESULTS, "Print validation results to console."));

        try {
            // if the application terminates too quickly, the framework is terminated before it was fully started
            // and some annoying stack traces are printed
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // do nothing
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.equinox.app.IApplication#stop()
     */
    @Override
    public void stop() {
    }

    ////////////////////////////////////////////////////////////

    private static final String PROJECT_FILE = ".project";

    /**
     * Checks the file if it belongs to a project and if that project has any linked resources. If it does have linked
     * resources, the entire structure is copied to a temp folder and the location of the file within the temp folder is
     * returned. If there are no linked resources, the file itself is returned.
     *
     * @param file the file to check if it belongs to a project and if there are any linked resources
     * @return the location that should be checked instead of the given file (could be the same)
     * @throws IOException
     * @throws URISyntaxException
     */
    private File setUpLocation(File file) throws IOException, URISyntaxException {
        File projectFolder = traverseToProjectFolder(file.getAbsoluteFile());
        Map<File, Link[]> links;
        File rootToCopy = file;
        if (projectFolder == null) {
            // this is a standalone file/folder, which certainly doesn't have any linked resources
            if (file.isFile()) {
                return file;
            }

            // if file is a directory, search for .project files.
            // gather all .project files that contains a linked resource
            links = gatherLinkedResources(new HashMap<>(), file);
            if (links.isEmpty()) {
                return file;
            }
        } else {
            links = gatherLinkedResources(new HashMap<>(), projectFolder);
            if (links.isEmpty()) {
                return file;
            }
            rootToCopy = projectFolder;
        }

        // if .project exists and at least one contains a linked resource tag, copy everything to a temp folder and
        // validate there
        String tempFolder = System.getProperty("java.io.tmpdir");
        File temp = new File(tempFolder, "opivalidation");
        temp = new File(temp, rootToCopy.getName());
        if (temp.exists()) {
            FileUtils.deleteDirectory(temp);
        }
        FileUtils.copyDirectory(rootToCopy, temp);
        deleteWhenFinished = true;

        String absoluteLocation = rootToCopy.getAbsolutePath();
        for (Map.Entry<File, Link[]> e : links.entrySet()) {
            File project = e.getKey().getParentFile();
            String absoluteProject = project.getAbsolutePath();
            if (!absoluteProject.startsWith(absoluteLocation)) {
                continue;
            }
            String path = absoluteProject.substring(absoluteLocation.length());
            File destination = new File(temp, path);
            for (Link r : e.getValue()) {
                File dest = new File(destination, r.getName());
                try {
                    if (r.getType() == 1) {
                        FileUtils.copyFile(r.getFile(), dest);
                    } else {
                        FileUtils.copyDirectory(r.getFile(), dest);
                    }
                } catch (FileNotFoundException ex) {
                    System.err.println("ERROR: " + ex.getMessage());
                }
            }
        }
        targetToDelete = temp;
        if (projectFolder != null) {
            String relative = file.getAbsolutePath().substring(projectFolder.getAbsolutePath().length());
            temp = new File(temp, relative);
        }
        return temp;
    }

    /**
     * Find if this file belongs to a project structure. If yes, the project folder (folder that contains the .project
     * file) is returned, otherwise null is returned.
     *
     * @param file the file to check if it belongs to a project
     * @return the project folder if it exists
     */
    private static File traverseToProjectFolder(File file) {
        if (file == null) {
            return null;
        } else if (file.isDirectory()) {
            String[] children = file.list();
            if (children != null) {
                for (String s : children) {
                    if (PROJECT_FILE.equals(s)) {
                        return file;
                    }
                }
            }
        }
        return traverseToProjectFolder(file.getParentFile());
    }

    /**
     * Checks the file and all its children for .project files and linked resources defined in those files.
     *
     * @param links the map to receive the links and should also be returned
     * @param file the base file to start the check with
     * @return the map containing all linked resources; key is the .project file, value is the array of links defined in
     *         that .project file
     */
    private static Map<File, Link[]> gatherLinkedResources(Map<File, Link[]> links, File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    if (f.isDirectory()) {
                        gatherLinkedResources(links, f);
                    } else if (PROJECT_FILE.equalsIgnoreCase(f.getName())) {
                        ArrayList<Link> linkList = new ArrayList<>();
                        ProjectDescription pd = JAXB.unmarshal(f, ProjectDescription.class);
                        Variable[] variables = pd.getVariables().toArray(new Variable[pd.getVariables().size()]);
                        for (Link l : pd.getLinks()) {
                            String locURI = l.getLocationURI();
                            String loc = l.getLocation();
                            for (Variable v : variables) {
                                if (locURI != null && locURI.contains(v.getName())) {
                                    locURI = locURI.replace(v.getName(), v.getValue());
                                }
                                if (loc != null && loc.contains(v.getName())) {
                                    loc = loc.replace(v.getName(), v.getValue());
                                }
                            }
                            l.setLocationURI(locURI);
                            l.setLocation(loc);
                            linkList.add(l);
                        }
                        if (!linkList.isEmpty()) {
                            links.put(f, linkList.toArray(new Link[linkList.size()]));
                        }
                    }
                }
            }
        }
        return links;
    }
}