Package com.technophobia.substeps.runner.syntax

Source Code of com.technophobia.substeps.runner.syntax.SubStepDefinitionParser

/*
*  Copyright Technophobia Ltd 2012
*
*   This file is part of Substeps.
*
*    Substeps is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Lesser General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    Substeps is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public License
*    along with Substeps.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.technophobia.substeps.runner.syntax;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Strings;
import com.technophobia.substeps.model.ParentStep;
import com.technophobia.substeps.model.PatternMap;
import com.technophobia.substeps.model.Step;
import com.technophobia.substeps.model.exception.DuplicatePatternException;
import com.technophobia.substeps.parser.FileContents;
import com.technophobia.substeps.runner.FeatureFileParser;

/**
* @author ian
*
*         TODO: failOnDuplicateSubsteps is used inconsistently between
*         processDirective and parseSubStepFile. It's also being interpreted as
*         'fail-fast' when it was intended to mean
*         parse-and-(run|don't-run)-on-error.
*
*/
public class SubStepDefinitionParser {

    private final Logger log = LoggerFactory.getLogger(SubStepDefinitionParser.class);

    private ParentStep currentParentStep;

    private final PatternMap<ParentStep> parentMap = new PatternMap<ParentStep>();

    private final boolean failOnDuplicateSubsteps;

    private final SyntaxErrorReporter syntaxErrorReporter;

    private FileContents currentFileContents;

    public SubStepDefinitionParser(final SyntaxErrorReporter syntaxErrorReporter) {
        this(true, syntaxErrorReporter);
    }

    public SubStepDefinitionParser(final boolean failOnDuplicateSubsteps, final SyntaxErrorReporter syntaxErrorReporter) {
        this.failOnDuplicateSubsteps = failOnDuplicateSubsteps;
        this.syntaxErrorReporter = syntaxErrorReporter;
    }

    void parseSubStepFile(final File substepFile) {

        this.currentFileContents = new FileContents();

        try {

            this.currentFileContents.readFile(substepFile);

            for (int i = 0; i < this.currentFileContents.getNumberOfLines(); i++) {

                // Line numbers are 1-based in FileContents
                processLine(i + 1);
            }

            // add the last scenario in, but only if it has some steps
            if (this.currentParentStep != null) {

                if (this.currentParentStep.getSteps() != null && !this.currentParentStep.getSteps().isEmpty()) {
                    try {
                        storeForPatternOrThrowException(this.currentParentStep.getParent().getPattern(),
                                this.currentParentStep);
                    } catch (final DuplicatePatternException ex) {
                        this.syntaxErrorReporter.reportSubstepsError(ex);
                        if (this.failOnDuplicateSubsteps) {
                            throw ex;
                        }
                    }
                } else {

                    this.log.warn("Ignoring substep definition [" + this.currentParentStep.getParent().getLine()
                            + "] as it has no steps");
                }
                // we're moving on to another file, so set this to null.
                // TODO - pass this around rather than stash the state
                this.currentParentStep = null;
            }
        } catch (final FileNotFoundException e) {
            this.log.error(e.getMessage(), e);
        } catch (final IOException e) {

            this.log.error(e.getMessage(), e);
        }
    }

    public PatternMap<ParentStep> loadSubSteps(final File definitions) {

        final List<File> substepsFiles = FileUtils.getFiles(definitions, ".substeps");

        for (final File f : substepsFiles) {
            parseSubStepFile(f);
        }

        return this.parentMap;
    }

    private void processLine(final int lineNumberIdx) {
        final String line = this.currentFileContents.getLineAt(lineNumberIdx);

        if (this.log.isTraceEnabled()) {
            this.log.trace("substep line[" + line + "] @ " + lineNumberIdx + ":"
                    + this.currentFileContents.getFile().getName());
        }

        if (line != null && line.length() > 0) {
            // does this line begin with any of annotation values that we're
            // interested in ?

            // pick out the first word
            final String trimmed = FeatureFileParser.stripComments(line.trim());
            if (trimmed != null && trimmed.length() > 0 && !trimmed.startsWith("#")) {
                processTrimmedLine(trimmed, lineNumberIdx);
            }

        }
    }

    private void processTrimmedLine(final String trimmed, final int lineNumberIdx) {

        // TODO convert <> into regex wildcards

        final int scolon = trimmed.indexOf(':');

        boolean lineProcessed = false;

        if (scolon > 0) {
            // is this a directive line
            final String word = trimmed.substring(0, scolon);
            final String remainder = trimmed.substring(scolon + 1);
            final Directive d = isDirective(word);
            if (d != null) {
                final String trimmedRemainder = remainder.trim();
                if (!Strings.isNullOrEmpty(trimmedRemainder)) {
                    processDirective(d, remainder, lineNumberIdx);
                    lineProcessed = true;
                }
            }
        }

        if (!lineProcessed) {
            if (this.currentParentStep != null) {

                final int sourceOffset = this.currentFileContents.getSourceStartOffsetForLineIndex(lineNumberIdx);
                // no context at the mo
                this.currentParentStep.addStep(new Step(trimmed, true, this.currentFileContents.getFile(),
                        lineNumberIdx, sourceOffset));
            }
        }
    }


    private void processDirective(final Directive d, final String remainder, final int lineNumberIdx) {
        this.currentDirective = d;

        switch (this.currentDirective) {

            case DEFINITION: {

                // build up a Step from the remainder

                final int sourceOffset = this.currentFileContents.getSourceStartOffsetForLineIndex(lineNumberIdx);

                final Step parent = new Step(remainder, true, this.currentFileContents.getFile(), lineNumberIdx,
                        sourceOffset);

                if (this.currentParentStep != null) {
                    final String newPattern = this.currentParentStep.getParent().getPattern();

                    try {
                        storeForPatternOrThrowException(newPattern, this.currentParentStep);
                    } catch (final DuplicatePatternException ex) {
                        this.syntaxErrorReporter.reportSubstepsError(ex);

                        if (this.failOnDuplicateSubsteps) {
                            throw ex;
                        }
                    }
                }

                this.currentParentStep = new ParentStep(parent);

                break;
            }
            default: // whatever
        }
    }

    /*
     * private void processDirective(final Directive d, final String remainder,
     * final File source, final int lineNumber) { this.currentDirective = d;
     *
     * switch (this.currentDirective) {
     *
     * case DEFINITION: {
     *
     * // build up a Step from the remainder
     *
     * final Step parent = new Step(remainder, true, source, lineNumber);
     *
     * if (this.currentParentStep != null) { final String newPattern =
     * this.currentParentStep.getParent() .getPattern(); // check for existing
     * values if (this.parentMap.containsPattern(newPattern)) { final ParentStep
     * otherValue = this.parentMap .getValueForPattern(newPattern);
     *
     * this.log.error("duplicate patterns detected: " + newPattern + " in : " +
     * otherValue.getSubStepFile() + " and " +
     * this.currentParentStep.getSubStepFile());
     *
     * }
     *
     * storeForPatternOrReportFailure(source, newPattern,
     * this.currentParentStep); }
     *
     * this.currentParentStep = new ParentStep(parent);
     *
     * break; } } }
     */

    private void storeForPatternOrThrowException(final String newPattern, final ParentStep parentStep)
            throws DuplicatePatternException {

        if (!this.parentMap.containsPattern(newPattern)) {
            this.parentMap.put(newPattern, parentStep);
        } else {
            throw new DuplicatePatternException(newPattern, this.parentMap.getValueForPattern(newPattern), parentStep);
        }

    }

    private static enum Directive {
        // @formatter:off
        DEFINITION("Define");

        // @formatter:on

        Directive(final String name) {
            this.name = name;
        }

        private final String name;
    }

    private Directive currentDirective = null;

    private Directive isDirective(final String word) {

        Directive rtn = null;

        for (final Directive d : Directive.values()) {

            if (word.equalsIgnoreCase(d.name)) {
                rtn = d;
                break;
            }
        }
        return rtn;
    }

}
TOP

Related Classes of com.technophobia.substeps.runner.syntax.SubStepDefinitionParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.