Keywords.java

  1. package org.jbehave.core.configuration;

  2. import static java.util.Arrays.asList;

  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.Map.Entry;
  7. import java.util.function.Predicate;
  8. import java.util.stream.Stream;

  9. import org.apache.commons.lang3.StringUtils;
  10. import org.apache.commons.lang3.builder.ToStringBuilder;
  11. import org.apache.commons.lang3.builder.ToStringStyle;
  12. import org.jbehave.core.steps.StepType;

  13. /**
  14.  * Provides the keywords which allow parsers to find steps in stories and match
  15.  * those steps with candidates through the annotations. It provides the starting
  16.  * words (Given, When, Then And, "!--") using in parsing, as well as providing
  17.  * keywords used in reporting.
  18.  */
  19. @SuppressWarnings("checkstyle:MemberName")
  20. public class Keywords {

  21.     private static final String SYNONYM_SEPARATOR = "\\|";
  22.    
  23.     public static final String META = "Meta";
  24.     public static final String META_PROPERTY = "MetaProperty";
  25.     public static final String NARRATIVE = "Narrative";
  26.     public static final String IN_ORDER_TO = "InOrderTo";
  27.     public static final String AS_A = "AsA";
  28.     public static final String I_WANT_TO = "IWantTo";
  29.     public static final String SO_THAT = "SoThat";
  30.     public static final String SCENARIO = "Scenario";
  31.     public static final String GIVEN_STORIES = "GivenStories";
  32.     public static final String LIFECYCLE = "Lifecycle";
  33.     public static final String BEFORE = "Before";
  34.     public static final String AFTER = "After";
  35.     public static final String EXAMPLES_TABLE = "ExamplesTable";
  36.     public static final String EXAMPLES_TABLE_ROW = "ExamplesTableRow";
  37.     public static final String EXAMPLES_TABLE_HEADER_SEPARATOR = "ExamplesTableHeaderSeparator";
  38.     public static final String EXAMPLES_TABLE_VALUE_SEPARATOR = "ExamplesTableValueSeparator";
  39.     public static final String EXAMPLES_TABLE_IGNORABLE_SEPARATOR = "ExamplesTableIgnorableSeparator";
  40.     public static final String GIVEN = "Given";
  41.     public static final String WHEN = "When";
  42.     public static final String THEN = "Then";
  43.     public static final String AND = "And";
  44.     public static final String IGNORABLE = "Ignorable";
  45.     public static final String COMPOSITE = "Composite";
  46.     public static final String PRIORITY = "Priority";
  47.     public static final String PENDING = "Pending";
  48.     public static final String NOT_PERFORMED = "NotPerformed";
  49.     public static final String FAILED = "Failed";
  50.     public static final String DRY_RUN = "DryRun";
  51.     public static final String STORY_CANCELLED = "StoryCancelled";
  52.     public static final String DURATION = "Duration";
  53.     public static final String SCOPE = "Scope";
  54.     public static final String SCOPE_STEP = "ScopeStep";
  55.     public static final String SCOPE_SCENARIO = "ScopeScenario";
  56.     public static final String SCOPE_STORY = "ScopeStory";
  57.     public static final String OUTCOME = "Outcome";
  58.     public static final String OUTCOME_ANY = "OutcomeAny";
  59.     public static final String OUTCOME_SUCCESS = "OutcomeSuccess";
  60.     public static final String OUTCOME_FAILURE = "OutcomeFailure";
  61.     public static final String OUTCOME_DESCRIPTION = "OutcomeDescription";
  62.     public static final String OUTCOME_VALUE = "OutcomeValue";
  63.     public static final String OUTCOME_MATCHER = "OutcomeMatcher";
  64.     public static final String OUTCOME_VERIFIED = "OutcomeVerified";
  65.     public static final String META_FILTER = "MetaFilter";
  66.     public static final String YES = "Yes";
  67.     public static final String NO = "No";

  68.     public static final List<String> KEYWORDS = asList(
  69.             META,
  70.             META_PROPERTY,
  71.             NARRATIVE,
  72.             IN_ORDER_TO,
  73.             AS_A,
  74.             I_WANT_TO,
  75.             SO_THAT,
  76.             SCENARIO,
  77.             GIVEN_STORIES,
  78.             LIFECYCLE,
  79.             BEFORE,
  80.             AFTER,
  81.             EXAMPLES_TABLE,
  82.             EXAMPLES_TABLE_ROW,
  83.             EXAMPLES_TABLE_HEADER_SEPARATOR,
  84.             EXAMPLES_TABLE_VALUE_SEPARATOR,
  85.             EXAMPLES_TABLE_IGNORABLE_SEPARATOR,
  86.             GIVEN,
  87.             WHEN,
  88.             THEN,
  89.             AND,
  90.             IGNORABLE,
  91.             COMPOSITE,
  92.             PRIORITY,
  93.             PENDING,
  94.             NOT_PERFORMED,
  95.             FAILED,
  96.             DRY_RUN,
  97.             STORY_CANCELLED,
  98.             DURATION,
  99.             SCOPE,
  100.             SCOPE_STEP,
  101.             SCOPE_SCENARIO,
  102.             SCOPE_STORY,
  103.             OUTCOME,
  104.             OUTCOME_ANY,
  105.             OUTCOME_SUCCESS,
  106.             OUTCOME_FAILURE,
  107.             OUTCOME_DESCRIPTION,
  108.             OUTCOME_VALUE,
  109.             OUTCOME_MATCHER,
  110.             OUTCOME_VERIFIED,
  111.             META_FILTER,
  112.             YES,
  113.             NO
  114.     );


  115.     private final String meta;
  116.     private final String metaProperty;
  117.     private final String narrative;
  118.     private final String inOrderTo;
  119.     private final String asA;
  120.     private final String iWantTo;
  121.     private final String soThat;
  122.     private final String scenario;
  123.     private final String givenStories;
  124.     private final String lifecycle;
  125.     private final String before;
  126.     private final String after;
  127.     private final String examplesTable;
  128.     private final String examplesTableRow;
  129.     private final String examplesTableHeaderSeparator;
  130.     private final String examplesTableValueSeparator;
  131.     private final String examplesTableIgnorableSeparator;
  132.     private final String given;
  133.     private final String when;
  134.     private final String then;
  135.     private final String and;
  136.     private final String ignorable;
  137.     private final String composite;
  138.     private final String priority;
  139.     private final String pending;
  140.     private final String notPerformed;
  141.     private final String failed;
  142.     private final String dryRun;
  143.     private final String storyCancelled;
  144.     private final String duration;
  145.     private final String scope;
  146.     private final String scopeStep;
  147.     private final String scopeScenario;
  148.     private final String scopeStory;
  149.     private final String outcome;
  150.     private final String outcomeAny;
  151.     private final String outcomeSuccess;
  152.     private final String outcomeFailure;
  153.     private final String outcomeDescription;
  154.     private final String outcomeValue;
  155.     private final String outcomeMatcher;
  156.     private final String outcomeVerified;
  157.     private final String metaFilter;
  158.     private final String yes;
  159.     private final String no;
  160.     private final Map<StepType, String> startingWordsByType = new HashMap<>();


  161.     public static Map<String, String> defaultKeywords() {
  162.         Map<String, String> keywords = new HashMap<>();
  163.         keywords.put(META, "Meta:");
  164.         keywords.put(META_PROPERTY, "@");
  165.         keywords.put(NARRATIVE, "Narrative:");
  166.         keywords.put(IN_ORDER_TO, "In order to");
  167.         keywords.put(AS_A, "As a");
  168.         keywords.put(I_WANT_TO, "I want to");
  169.         keywords.put(SO_THAT, "So that");
  170.         keywords.put(SCENARIO, "Scenario:");
  171.         keywords.put(GIVEN_STORIES, "GivenStories:");
  172.         keywords.put(LIFECYCLE, "Lifecycle:");
  173.         keywords.put(BEFORE, "Before:");
  174.         keywords.put(AFTER, "After:");
  175.         keywords.put(EXAMPLES_TABLE, "Examples:");
  176.         keywords.put(EXAMPLES_TABLE_ROW, "Example:");
  177.         keywords.put(EXAMPLES_TABLE_HEADER_SEPARATOR, "|");
  178.         keywords.put(EXAMPLES_TABLE_VALUE_SEPARATOR, "|");
  179.         keywords.put(EXAMPLES_TABLE_IGNORABLE_SEPARATOR, "|--");
  180.         keywords.put(GIVEN, "Given");
  181.         keywords.put(WHEN, "When");
  182.         keywords.put(THEN, "Then");
  183.         keywords.put(AND, "And");
  184.         keywords.put(IGNORABLE, "!--");
  185.         keywords.put(COMPOSITE, "Composite:");
  186.         keywords.put(PRIORITY, "Priority:");
  187.         keywords.put(PENDING, "PENDING");
  188.         keywords.put(NOT_PERFORMED, "NOT PERFORMED");
  189.         keywords.put(FAILED, "FAILED");
  190.         keywords.put(DRY_RUN, "DRY RUN");
  191.         keywords.put(STORY_CANCELLED, "STORY CANCELLED");
  192.         keywords.put(DURATION, "DURATION");
  193.         keywords.put(SCOPE, "Scope:");
  194.         keywords.put(SCOPE_STEP, "STEP");
  195.         keywords.put(SCOPE_SCENARIO, "SCENARIO");
  196.         keywords.put(SCOPE_STORY, "STORY");
  197.         keywords.put(OUTCOME, "Outcome:");
  198.         keywords.put(OUTCOME_ANY, "ANY");
  199.         keywords.put(OUTCOME_SUCCESS, "SUCCESS");
  200.         keywords.put(OUTCOME_FAILURE, "FAILURE");
  201.         keywords.put(OUTCOME_DESCRIPTION, "DESCRIPTION");
  202.         keywords.put(OUTCOME_MATCHER, "MATCHER");
  203.         keywords.put(OUTCOME_VALUE, "VALUE");
  204.         keywords.put(OUTCOME_VERIFIED, "VERIFIED");
  205.         keywords.put(META_FILTER, "MetaFilter:");
  206.         keywords.put(YES, "Yes");
  207.         keywords.put(NO, "No");
  208.         return keywords;
  209.     }

  210.     /**
  211.      * Creates Keywords with default values {@link #defaultKeywords()}
  212.      */
  213.     public Keywords() {
  214.         this(defaultKeywords());
  215.     }

  216.     /**
  217.      * Creates Keywords with provided keywords Map and Encoding
  218.      *
  219.      * @param keywords the Map of keywords indexed by their name
  220.      */
  221.     public Keywords(Map<String, String> keywords) {
  222.         this.meta = keyword(META, keywords);
  223.         this.metaProperty = keyword(META_PROPERTY, keywords);
  224.         this.narrative = keyword(NARRATIVE, keywords);
  225.         this.inOrderTo = keyword(IN_ORDER_TO, keywords);
  226.         this.asA = keyword(AS_A, keywords);
  227.         this.iWantTo = keyword(I_WANT_TO, keywords);
  228.         this.soThat = keyword(SO_THAT, keywords);
  229.         this.scenario = keyword(SCENARIO, keywords);
  230.         this.givenStories = keyword(GIVEN_STORIES, keywords);
  231.         this.lifecycle = keyword(LIFECYCLE, keywords);
  232.         this.before = keyword(BEFORE, keywords);
  233.         this.after = keyword(AFTER, keywords);
  234.         this.examplesTable = keyword(EXAMPLES_TABLE, keywords);
  235.         this.examplesTableRow = keyword(EXAMPLES_TABLE_ROW, keywords);
  236.         this.examplesTableHeaderSeparator = keyword(EXAMPLES_TABLE_HEADER_SEPARATOR, keywords);
  237.         this.examplesTableValueSeparator = keyword(EXAMPLES_TABLE_VALUE_SEPARATOR, keywords);
  238.         this.examplesTableIgnorableSeparator = keyword(EXAMPLES_TABLE_IGNORABLE_SEPARATOR, keywords);
  239.         this.given = keyword(GIVEN, keywords);
  240.         this.when = keyword(WHEN, keywords);
  241.         this.then = keyword(THEN, keywords);
  242.         this.and = keyword(AND, keywords);
  243.         this.ignorable = keyword(IGNORABLE, keywords);
  244.         this.composite = keyword(COMPOSITE, keywords);
  245.         this.priority = keyword(PRIORITY, keywords);
  246.         this.pending = keyword(PENDING, keywords);
  247.         this.notPerformed = keyword(NOT_PERFORMED, keywords);
  248.         this.failed = keyword(FAILED, keywords);
  249.         this.dryRun = keyword(DRY_RUN, keywords);
  250.         this.storyCancelled = keyword(STORY_CANCELLED, keywords);
  251.         this.duration = keyword(DURATION, keywords);
  252.         this.scope = keyword(SCOPE, keywords);
  253.         this.scopeStep = keyword(SCOPE_STEP, keywords);
  254.         this.scopeScenario = keyword(SCOPE_SCENARIO, keywords);
  255.         this.scopeStory = keyword(SCOPE_STORY, keywords);
  256.         this.outcome = keyword(OUTCOME, keywords);
  257.         this.outcomeAny = keyword(OUTCOME_ANY, keywords);
  258.         this.outcomeSuccess = keyword(OUTCOME_SUCCESS, keywords);
  259.         this.outcomeFailure = keyword(OUTCOME_FAILURE, keywords);
  260.         this.outcomeDescription = keyword(OUTCOME_DESCRIPTION, keywords);
  261.         this.outcomeMatcher = keyword(OUTCOME_MATCHER, keywords);
  262.         this.outcomeValue = keyword(OUTCOME_VALUE, keywords);
  263.         this.outcomeVerified = keyword(OUTCOME_VERIFIED, keywords);
  264.         this.metaFilter = keyword(META_FILTER, keywords);
  265.         this.yes = keyword(YES, keywords);
  266.         this.no = keyword(NO, keywords);

  267.         startingWordsByType.put(StepType.GIVEN, given());
  268.         startingWordsByType.put(StepType.WHEN, when());
  269.         startingWordsByType.put(StepType.THEN, then());
  270.         startingWordsByType.put(StepType.AND, and());
  271.         startingWordsByType.put(StepType.IGNORABLE, ignorable());

  272.     }

  273.     private String keyword(String name, Map<String, String> keywords) {
  274.         String keyword = keywords.get(name);
  275.         if (keyword == null) {
  276.             throw new KeywordNotFound(name, keywords);
  277.         }
  278.         return keyword;
  279.     }

  280.     public String meta() {
  281.         return meta;
  282.     }

  283.     public String metaProperty() {
  284.         return metaProperty;
  285.     }

  286.     public String narrative() {
  287.         return narrative;
  288.     }

  289.     public String inOrderTo() {
  290.         return inOrderTo;
  291.     }

  292.     public String asA() {
  293.         return asA;
  294.     }

  295.     @SuppressWarnings("checkstyle:MethodName")
  296.     public String iWantTo() {
  297.         return iWantTo;
  298.     }

  299.     public String soThat() {
  300.         return soThat;
  301.     }

  302.     public String scenario() {
  303.         return scenario;
  304.     }

  305.     public String givenStories() {
  306.         return givenStories;
  307.     }

  308.     public String lifecycle() {
  309.         return lifecycle;
  310.     }

  311.     public String before() {
  312.         return before;
  313.     }

  314.     public String after() {
  315.         return after;
  316.     }

  317.     public String examplesTable() {
  318.         return examplesTable;
  319.     }

  320.     public String examplesTableRow() {
  321.         return examplesTableRow;
  322.     }

  323.     public String examplesTableHeaderSeparator() {
  324.         return examplesTableHeaderSeparator;
  325.     }

  326.     public String examplesTableValueSeparator() {
  327.         return examplesTableValueSeparator;
  328.     }

  329.     public String examplesTableIgnorableSeparator() {
  330.         return examplesTableIgnorableSeparator;
  331.     }

  332.     public String given() {
  333.         return given;
  334.     }

  335.     public String when() {
  336.         return when;
  337.     }

  338.     public String then() {
  339.         return then;
  340.     }

  341.     public String and() {
  342.         return and;
  343.     }

  344.     public String ignorable() {
  345.         return ignorable;
  346.     }

  347.     public String composite() {
  348.         return composite;
  349.     }

  350.     public String priority() {
  351.         return priority;
  352.     }

  353.     public String pending() {
  354.         return pending;
  355.     }

  356.     public String notPerformed() {
  357.         return notPerformed;
  358.     }

  359.     public String failed() {
  360.         return failed;
  361.     }

  362.     public String dryRun() {
  363.         return dryRun;
  364.     }

  365.     public String storyCancelled() {
  366.         return storyCancelled;
  367.     }

  368.     public String duration() {
  369.         return duration;
  370.     }

  371.     public String scope() {
  372.         return scope;
  373.     }

  374.     public String scopeStep() {
  375.         return scopeStep;
  376.     }

  377.     public String scopeScenario() {
  378.         return scopeScenario;
  379.     }

  380.     public String scopeStory() {
  381.         return scopeStory;
  382.     }

  383.     public String outcome() {
  384.         return outcome;
  385.     }

  386.     public String outcomeAny() {
  387.         return outcomeAny;
  388.     }

  389.     public String outcomeSuccess() {
  390.         return outcomeSuccess;
  391.     }

  392.     public String outcomeFailure() {
  393.         return outcomeFailure;
  394.     }

  395.     public String outcomeDescription() {
  396.         return outcomeDescription;
  397.     }

  398.     public String outcomeValue() {
  399.         return outcomeValue;
  400.     }

  401.     public String outcomeMatcher() {
  402.         return outcomeMatcher;
  403.     }

  404.     public String outcomeVerified() {
  405.         return outcomeVerified;
  406.     }

  407.     public List<String> outcomeFields() {
  408.         return asList(outcomeDescription, outcomeValue, outcomeMatcher, outcomeVerified);
  409.     }

  410.     public String metaFilter() {
  411.         return metaFilter;
  412.     }

  413.     public String yes() {
  414.         return yes;
  415.     }

  416.     public String no() {
  417.         return no;
  418.     }

  419.     public String[] synonymsOf(String word) {
  420.         return word.split(SYNONYM_SEPARATOR);
  421.     }

  422.     public Stream<String> startingWords(Predicate<StepType> stepTypeFilter) {
  423.         return startingWordsByType()
  424.                 .entrySet()
  425.                 .stream()
  426.                 .filter(e -> stepTypeFilter.test(e.getKey()))
  427.                 .map(Entry::getValue)
  428.                 .map(this::synonymsOf)
  429.                 .flatMap(Stream::of);
  430.     }

  431.     public Map<StepType, String> startingWordsByType() {
  432.         return startingWordsByType;
  433.     }

  434.     private boolean ofStepType(String stepAsString, StepType stepType) {
  435.         boolean isType = false;
  436.         for (String word : startingWordsFor(stepType)) {
  437.             isType = stepStartsWithWord(stepAsString, word);
  438.             if (isType) {
  439.                 break;
  440.             }
  441.         }
  442.         return isType;
  443.     }

  444.     public boolean isAndStep(String stepAsString) {
  445.         return ofStepType(stepAsString, StepType.AND);
  446.     }

  447.     public boolean isIgnorableStep(String stepAsString) {
  448.         return ofStepType(stepAsString, StepType.IGNORABLE);
  449.     }

  450.     public String stepWithoutStartingWord(String stepAsString, StepType stepType) {
  451.         String startingWord = startingWord(stepAsString, stepType);
  452.         return stepAsString.substring(startingWord.length() + 1); // 1 for the space after
  453.     }

  454.     public String stepWithoutStartingWord(String stepAsString) {
  455.         StepType stepType = stepTypeFor(stepAsString);
  456.         return stepWithoutStartingWord(stepAsString, stepType);
  457.     }

  458.     public String startingWord(String stepAsString, StepType stepType) throws StartingWordNotFound {
  459.         for (String wordForType : startingWordsFor(stepType)) {
  460.             if (stepStartsWithWord(stepAsString, wordForType)) {
  461.                 return wordForType;
  462.             }
  463.         }
  464.         for (String andWord : startingWordsFor(StepType.AND)) {
  465.             if (stepStartsWithWord(stepAsString, andWord)) {
  466.                 return andWord;
  467.             }
  468.         }
  469.         throw new StartingWordNotFound(stepAsString, stepType, startingWordsByType);
  470.     }

  471.     public String startingWord(String stepAsString) throws StartingWordNotFound {
  472.         for (StepType stepType : startingWordsByType.keySet()) {
  473.             for (String wordForType : startingWordsFor(stepType)) {
  474.                 if (stepStartsWithWord(stepAsString, wordForType)) {
  475.                     return wordForType;
  476.                 }
  477.             }
  478.         }
  479.         throw new StartingWordNotFound(stepAsString, startingWordsByType);
  480.     }

  481.     public StepType stepTypeFor(String stepAsString) throws StartingWordNotFound {
  482.         for (StepType stepType : startingWordsByType.keySet()) {
  483.             for (String wordForType : startingWordsFor(stepType)) {
  484.                 if (stepStartsWithWord(stepAsString, wordForType)) {
  485.                     return stepType;
  486.                 }
  487.             }
  488.         }
  489.         throw new StartingWordNotFound(stepAsString, startingWordsByType);
  490.     }

  491.     public boolean stepStartsWithWord(String step, String word) {
  492.         return stepStartsWithWords(step, word);
  493.     }

  494.     public boolean stepStartsWithWords(String step, String... words) {
  495.         char separator = ' '; // space after qualifies it as word
  496.         String start = StringUtils.join(words, separator) + separator;
  497.         return step.startsWith(start);
  498.     }

  499.     public String startingWordFor(StepType stepType) {
  500.         String startingWord = startingWordsByType.get(stepType);
  501.         if (startingWord == null) {
  502.             throw new StartingWordNotFound(stepType, startingWordsByType);
  503.         }
  504.         return startingWord;
  505.     }

  506.     public String[] startingWordsFor(StepType stepType) {
  507.         return synonymsOf(startingWordFor(stepType));
  508.     }

  509.     @Override
  510.     public String toString() {
  511.         return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
  512.     }

  513.     @SuppressWarnings("serial")
  514.     public static class KeywordNotFound extends RuntimeException {

  515.         public KeywordNotFound(String name, Map<String, String> keywords) {
  516.             super("Keyword " + name + " not found amongst " + keywords);
  517.         }

  518.     }

  519.     @SuppressWarnings("serial")
  520.     public static class StartingWordNotFound extends RuntimeException {

  521.         public StartingWordNotFound(String step, StepType stepType, Map<StepType, String> startingWordsByType) {
  522.             super("No starting word found for step '" + step + "' of type '" + stepType + "' amongst '"
  523.                     + startingWordsByType + "'");
  524.         }

  525.         public StartingWordNotFound(String step, Map<StepType, String> startingWordsByType) {
  526.             super("No starting word found for step '" + step + "' amongst '" + startingWordsByType + "'");
  527.         }

  528.         public StartingWordNotFound(StepType stepType, Map<StepType, String> startingWordsByType) {
  529.             super("No starting word found of type '" + stepType + "' amongst '" + startingWordsByType + "'");
  530.         }
  531.     }
  532. }