All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.technophobia.substeps.model.Syntax Maven / Gradle / Ivy

There is a newer version: 1.1.8
Show newest version
/*
 *  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 .
 */
package com.technophobia.substeps.model;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.technophobia.substeps.model.exception.DuplicateStepImplementationException;
import com.technophobia.substeps.model.exception.StepImplementationException;
import com.technophobia.substeps.model.exception.UnimplementedStepException;
import com.technophobia.substeps.runner.syntax.DefaultSyntaxErrorReporter;
import com.technophobia.substeps.runner.syntax.SyntaxErrorReporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.PatternSyntaxException;

/**
 * @author imoore
 */
public class Syntax {

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

    // These two will always be populated
    private final Map> stepImplementationMap = new HashMap>();

    private final Map> stepImplementationsUsageMap = new HashMap>();

    // this is the map of substeps - Define: blah blah and might not be populated
    private PatternMap subStepsMap = null;

    private boolean strict;
    private boolean failOnDuplicateStepImplementations = true;
    private String[] nonStrictKeywordPrecedence;

    private final SyntaxErrorReporter syntaxErrorReporter;


    public Syntax() {
        this(new DefaultSyntaxErrorReporter());
    }


    public Syntax(final SyntaxErrorReporter syntaxErrorReporter) {
        this.syntaxErrorReporter = syntaxErrorReporter;
    }

    public Map> getStepImplementationMap() {
        return this.stepImplementationMap;
    }


    public List getStepImplementations() {
        // build a list of the impls in the order of the annotations
        final List allImpls = new ArrayList();

        final List sortedAnnotations = new ArrayList();
        sortedAnnotations.addAll(this.stepImplementationMap.keySet());

        Collections.sort(sortedAnnotations);

        // get all the PatternMaps for each annotation:

        for (final String annotation : sortedAnnotations) {
            final PatternMap patternMap = this.stepImplementationMap.get(annotation);

            if (patternMap != null) {
                for (final StepImplementation impl : patternMap.values()) {
                    allImpls.add(impl);
                }
            }
        }
        return allImpls;
    }


    private PatternMap getPatternMapForAnnotation(final String keyWord) {
        return this.stepImplementationMap.get(keyWord);
    }


    public void setSubStepsMap(final PatternMap loadSubSteps) {
        this.subStepsMap = loadSubSteps;
    }


    public PatternMap getSubStepsMap() {
        return this.subStepsMap;
    }


    public List getSortedRootSubSteps() {

        final List sortedList = new ArrayList();

        // this can be null if no substeps files are provided - this is legit behaviour..
        if (this.subStepsMap != null) {
            final Collection rootSubSteps = this.subStepsMap.values();
            sortedList.addAll(rootSubSteps);
            Collections.sort(sortedList, ParentStep.PARENT_STEP_COMPARATOR);
        }
        return sortedList;
    }


    public void addStepImplementation(final StepImplementation impl) {

        stepImplementationsUsageMap.put(impl, new ArrayList());

        PatternMap patternMap = this.stepImplementationMap.get(impl.getKeyword());

        if (patternMap == null) {
            patternMap = new PatternMap();
            this.stepImplementationMap.put(impl.getKeyword(), patternMap);
        }

        final String pattern = impl.getValue();
        if (!patternMap.containsPattern(pattern)) {

            try {
                patternMap.put(pattern, impl);
            } catch (final PatternSyntaxException e) {

                log.warn("Invalid step implementation pattern: " + e.getMessage() + "\n" + impl.getClass() + "."
                        + impl.getMethod() + " will not be added to the syntax");
            }

        } else {

            final StepImplementation implAlreadyGot = patternMap.getValueForPattern(pattern);

            if (!implAlreadyGot.getMethod().equals(impl.getMethod())) {

                final StepImplementationException ex = new DuplicateStepImplementationException(pattern,
                        patternMap.getValueForPattern(pattern), impl);
                this.syntaxErrorReporter.reportStepImplError(ex);
                if (this.failOnDuplicateStepImplementations) {
                    throw ex;
                }
            } else {
                // this is the same - no need to make a fuss, just ignore it
            }
        }

    }


    public void setStrict(final boolean strict, final String[] nonStrictKeywordPrecedence) {
        this.strict = strict;
        this.nonStrictKeywordPrecedence = nonStrictKeywordPrecedence;

        if (!strict && (this.nonStrictKeywordPrecedence == null || this.nonStrictKeywordPrecedence.length == 0)) {
            throw new IllegalArgumentException(
                    "Please provide a keyword precedence in parameter nonStrictKeywordPrecedence to use when running in non strict mode");
        }
    }


    public void setFailOnDuplicateStepImplementations(final boolean failOnDuplicateStepImplementations) {
        this.failOnDuplicateStepImplementations = failOnDuplicateStepImplementations;
    }


    public List getStepImplementations(final String keyword, final String parameterLine,
                                                           final File source, final int lineNumber) {
        return getStepImplementationsInternal(keyword, parameterLine, false, source, lineNumber);
    }


    public List checkForStepImplementations(final String keyword, final String parameterLine,
                                                                final File source, final int lineNumber) {
        return getStepImplementationsInternal(keyword, parameterLine, true, source, lineNumber);
    }


    private List getStepImplementationsInternal(final String keyword, final String parameterLine,
                                                                    final boolean okNotTofindAnything, final File source, final int lineNumber) {
        List list = getStrictStepimplementation(keyword, parameterLine, okNotTofindAnything,
                source, lineNumber);

        if (!this.strict
                && ((list == null && okNotTofindAnything) || (!okNotTofindAnything && list != null && list.isEmpty()))) {
            // look for an alternative, iterate through the
            // nonStrictKeywordPrecedence until we get what we want

            for (final String altKeyword : this.nonStrictKeywordPrecedence) {
                // don't use the same keyword again
                if (altKeyword.compareToIgnoreCase(keyword) != 0) {
                    final List altStepImplementations = getStrictStepimplementation(altKeyword,
                            parameterLine.replaceFirst(keyword, altKeyword), okNotTofindAnything, source, lineNumber);
                    if (altStepImplementations != null && !altStepImplementations.isEmpty()) {
                        // found an alternative, bail immediately
                        list = new ArrayList(Collections2.transform(altStepImplementations,
                                new CloneStepImplementationsWithNewKeywordFunction(keyword)));
                        break;
                    }
                }
            }
        }

        if (list != null) {
            for (final StepImplementation s : list) {
                stepImplementationsUsageMap.get(s).add(new StepImplementationUsage(parameterLine, source, lineNumber));
            }
        }

        return list;
    }


    private List getStrictStepimplementation(final String keyword, final String parameterLine,
                                                                 final boolean okNotTofindAnything, final File source, final int lineNumber) {

        List list = null;

        final PatternMap pMap = getPatternMapForAnnotation(keyword);

        if (pMap != null) {
            list = pMap.get(parameterLine);
        } else if (!okNotTofindAnything) {
            throw new UnimplementedStepException(parameterLine, source, lineNumber);
        }

        return list;
    }


    private static final class CloneStepImplementationsWithNewKeywordFunction implements
            Function {

        private final String keyword;

        public CloneStepImplementationsWithNewKeywordFunction(final String keyword) {
            this.keyword = keyword;
        }

        @Override
        public StepImplementation apply(final StepImplementation stepImplementation) {

            return stepImplementation != null ? stepImplementation.cloneWithKeyword(this.keyword) : null;
        }
    }


    /**
     * @return the strict
     */
    public boolean isStrict() {
        return this.strict;
    }


    /**
     * @return the nonStrictKeywordPrecedence
     */
    public String[] getNonStrictKeywordPrecedence() {
        return this.nonStrictKeywordPrecedence;
    }


    public List getUncalledStepImplementations() {

        final List uncalled = new ArrayList();
        final Set>> entrySet = stepImplementationsUsageMap
                .entrySet();

        for (final Entry> e : entrySet) {

            if (e.getValue().isEmpty()) {
                uncalled.add(e.getKey());
            }
        }
        return uncalled;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy