JsonOutput.java

  1. package org.jbehave.core.reporters;

  2. import static org.jbehave.core.reporters.PrintStreamOutput.Format.JSON;

  3. import java.io.PrintStream;
  4. import java.util.Properties;

  5. import org.apache.commons.lang3.ArrayUtils;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.jbehave.core.configuration.Keywords;

  8. /**
  9.  * <p>
  10.  * Story reporter that outputs to a PrintStream, as JSON. It extends
  11.  * {@link PrintStreamOutput}, providing JSON-based default output
  12.  * patterns, which can be overridden via the {@link
  13.  * JsonOutput (PrintStream,Properties)} constructor.
  14.  * </p>
  15.  *
  16.  * @author Valery Yatsynovich
  17.  */
  18. public class JsonOutput extends PrintStreamOutput {

  19.     private static final char JSON_DOCUMENT_START = 0;
  20.     private static final char JSON_OBJECT_START = '{';
  21.     private static final char JSON_ARRAY_START = '[';
  22.     private static final char[] JSON_START_CHARS = { JSON_DOCUMENT_START, JSON_OBJECT_START, JSON_ARRAY_START };

  23.     private static final String[] STEP_KEYS = { "successful", "ignorable", "comment", "pending", "notPerformed",
  24.         "failed", "restarted" };

  25.     private char lastChar = JSON_DOCUMENT_START;

  26.     private int givenStoriesLevel = 0;
  27.     private int storyPublishingLevel = 0;
  28.     private boolean stepPublishing = false;

  29.     public JsonOutput(PrintStream output, Keywords keywords) {
  30.         this(output, new Properties(), keywords);
  31.     }

  32.     public JsonOutput(PrintStream output, Properties outputPatterns, Keywords keywords) {
  33.         super(JSON, output, defaultXmlPatterns(), outputPatterns, keywords);
  34.     }

  35.     @Override
  36.     protected void print(PrintStream output, String text) {
  37.         if (!text.isEmpty()) {
  38.             boolean doNotAddComma =
  39.                     ArrayUtils.contains(JSON_START_CHARS, lastChar) || StringUtils.startsWithAny(text, "}", "]", ",");
  40.             super.print(output, doNotAddComma ? text : "," + text);
  41.             lastChar = text.charAt(text.length() - 1);
  42.         }
  43.     }

  44.     @Override
  45.     protected String format(String key, String defaultPattern, Object... args) {
  46.         if ("beforeGivenStories".equals(key)) {
  47.             givenStoriesLevel++;
  48.         } else if ("afterGivenStories".equals(key)) {
  49.             if (storyPublishingLevel == givenStoriesLevel) {
  50.                 // Closing given "stories"
  51.                 print("]");
  52.                 storyPublishingLevel--;
  53.             }
  54.             givenStoriesLevel--;
  55.             return super.format(key, defaultPattern, args);
  56.         } else if ("beforeStory".equals(key) && storyPublishingLevel < givenStoriesLevel) {
  57.             // Starting given "stories"
  58.             print("\"stories\": [");
  59.             storyPublishingLevel++;
  60.         }
  61.         //Closing "examples" if "steps" are empty
  62.         if ("exampleScenariosEnd".equals(key) && !stepPublishing) {
  63.             print("}");
  64.         }

  65.         if (key.startsWith("afterBefore")) {
  66.             stepPublishing = false;
  67.         }

  68.         if (stepPublishing) {
  69.             if ("exampleScenariosEnd".equals(key) || "example".equals(key)) {
  70.                 // Closing previous "example"
  71.                 print("}");
  72.             }
  73.             if ("afterScenario".equals(key) || "afterScenarioWithFailure".equals(key)) {
  74.                 stepPublishing = false;
  75.             }
  76.         } else if (ArrayUtils.contains(STEP_KEYS, key)) {
  77.             stepPublishing = true;
  78.         }
  79.         return super.format(key, defaultPattern, args);
  80.     }

  81.     @Override
  82.     protected String transformPrintingTable(String text, String tableStart, String tableEnd) {
  83.         return text;
  84.     }

  85.     @SuppressWarnings("checkstyle:LineLength")
  86.     private static Properties defaultXmlPatterns() {
  87.         Properties patterns = new Properties();
  88.         patterns.setProperty("dryRun", "\"dryRun\": \"{0}\"");
  89.         patterns.setProperty("beforeStory", "'{'\"id\": \"{0}\", \"path\": \"{2}\", \"title\": \"{1}\"");
  90.         patterns.setProperty("storyCancelled", "'{'\"cancelled\": '{'\"keyword\": \"{0}\", \"durationKeyword\": \"{1}\", \"durationInSecs\": \"{2}\"}}");
  91.         patterns.setProperty("afterStory", "}");
  92.         patterns.setProperty("beforeScenarios", "\"scenarios\": [");
  93.         patterns.setProperty("afterScenarios", "]");
  94.         patterns.setProperty("afterStory", "}");
  95.         patterns.setProperty("metaStart", "\"meta\": '['");
  96.         patterns.setProperty("metaProperty", "'{'\"keyword\": \"{0}\", \"name\": \"{1}\", \"value\": \"{2}\"}");
  97.         patterns.setProperty("metaEnd", "']'");
  98.         patterns.setProperty("filter", "\"filter\": \"{0}\"");
  99.         patterns.setProperty("narrative", "\"narrative\": '{'\"keyword\": \"{0}\",  \"inOrderTo\": '{'\"keyword\": \"{1}\", \"value\": \"{2}\"}, \"asA\": '{'\"keyword\": \"{3}\", \"value\": \"{4}\"}, \"iWantTo\": '{'\"keyword\": \"{5}\", \"value\": \"{6}\"}}");
  100.         patterns.setProperty("lifecycleStart", "\"lifecycle\": '{'\"keyword\": \"{0}\"");
  101.         patterns.setProperty("lifecycleEnd", "}");
  102.         patterns.setProperty("lifecycleBeforeStart", "\"before\": '{'\"keyword\": \"{0}\", \"scopes\": [");
  103.         patterns.setProperty("lifecycleBeforeEnd", "]}");
  104.         patterns.setProperty("lifecycleAfterStart", "\"after\": '{'\"keyword\": \"{0}\", \"scopes\": [");
  105.         patterns.setProperty("lifecycleAfterEnd", "]}");
  106.         patterns.setProperty("lifecycleBeforeScopeStart", "'{'\"keyword\": \"{0}\", \"value\": \"{1}\", \"steps\": [");
  107.         patterns.setProperty("lifecycleBeforeScopeEnd", "]}");
  108.         patterns.setProperty("lifecycleAfterScopeStart", "'{'\"keyword\": \"{0}\", \"value\": \"{1}\", \"outcomes\": [");
  109.         patterns.setProperty("lifecycleAfterScopeEnd", "]}");
  110.         patterns.setProperty("lifecycleOutcomeStart", "'{'\"keyword\": \"{0}\", \"value\": \"{1}\", \"steps\": [");
  111.         patterns.setProperty("lifecycleOutcomeEnd", "]}");
  112.         patterns.setProperty("lifecycleMetaFilter", "\"metaFilter\": \"{0} {1}\"");
  113.         patterns.setProperty("lifecycleStep", "\"{0}\"");
  114.         patterns.setProperty("beforeBeforeStoriesSteps", "'{'\"beforeStoriesSteps\": [");
  115.         patterns.setProperty("afterBeforeStoriesSteps", "]}");
  116.         patterns.setProperty("beforeAfterStoriesSteps", "'{'\"afterStoriesSteps\": [");
  117.         patterns.setProperty("afterAfterStoriesSteps", "]}");
  118.         patterns.setProperty("beforeBeforeSystemStorySteps", "\"beforeSystemStorySteps\": [");
  119.         patterns.setProperty("afterBeforeSystemStorySteps", "]");
  120.         patterns.setProperty("beforeAfterSystemStorySteps", "\"afterSystemStorySteps\": [");
  121.         patterns.setProperty("afterAfterSystemStorySteps", "]");
  122.         patterns.setProperty("beforeBeforeUserStorySteps", "\"beforeUserStorySteps\": [");
  123.         patterns.setProperty("afterBeforeUserStorySteps", "]");
  124.         patterns.setProperty("beforeAfterUserStorySteps", "\"afterUserStorySteps\": [");
  125.         patterns.setProperty("afterAfterUserStorySteps", "]");
  126.         patterns.setProperty("beforeBeforeSystemScenarioSteps", "\"beforeSystemScenarioSteps\": [");
  127.         patterns.setProperty("afterBeforeSystemScenarioSteps", "]");
  128.         patterns.setProperty("beforeAfterSystemScenarioSteps", "\"afterSystemScenarioSteps\": [");
  129.         patterns.setProperty("afterAfterSystemScenarioSteps", "]");
  130.         patterns.setProperty("beforeBeforeUserScenarioSteps", "\"beforeUserScenarioSteps\": [");
  131.         patterns.setProperty("afterBeforeUserScenarioSteps", "]");
  132.         patterns.setProperty("beforeAfterUserScenarioSteps", "\"afterUserScenarioSteps\": [");
  133.         patterns.setProperty("afterAfterUserScenarioSteps", "]");
  134.         patterns.setProperty("beforeScenarioSteps", "\"steps\": [");
  135.         patterns.setProperty("afterScenarioSteps", "]");
  136.         patterns.setProperty("beforeScenario","'{'\"keyword\": \"{1}\", \"id\": \"{0}\", \"title\": \"{2}\"");
  137.         patterns.setProperty("scenarioExcluded", "\"excluded\": '{'\"pattern\": \"{0}\"}");
  138.         patterns.setProperty("afterScenario", "}");
  139.         patterns.setProperty("afterScenarioWithFailure", "\"failure\": \"{0}\" }");
  140.         patterns.setProperty("givenStories", "\"givenStories\": '{'\"keyword\": \"{0}\", \"paths\": \"{1}\"}");
  141.         patterns.setProperty("beforeGivenStories", "\"givenStories\": '{'");
  142.         patterns.setProperty("givenStoriesStart", "\"keyword\": \"{0}\", \"givenStories\":[");
  143.         patterns.setProperty("givenStory", "'{'\"parameters\": \"{1}\", \"path\": \"{0}\"}");
  144.         patterns.setProperty("givenStoriesEnd", "]");
  145.         patterns.setProperty("afterGivenStories", "}");
  146.         patterns.setProperty("beforeStep", "'{'");
  147.         patterns.setProperty("successful", "\"outcome\": \"successful\", \"value\": \"{0}\"}");
  148.         patterns.setProperty("ignorable", "\"outcome\": \"ignorable\", \"value\": \"{0}\"}");
  149.         patterns.setProperty("comment", "\"outcome\": \"comment\", \"value\": \"{0}\"}");
  150.         patterns.setProperty("pending", "\"outcome\": \"pending\", \"keyword\": \"{1}\", \"value\": \"{0}\", \"pendingMethod\": \"{2}\"}");
  151.         patterns.setProperty("notPerformed", "\"outcome\": \"notPerformed\", \"keyword\": \"{1}\", \"value\": \"{0}\"}");
  152.         patterns.setProperty("failed", "\"outcome\": \"failed\", \"keyword\": \"{1}\", \"value\": \"{0}\", \"failure\": \"{2}\"}");
  153.         patterns.setProperty("restarted", "\"outcome\": \"restarted\", \"value\": \"{0}\", \"reason\": \"{1}\"}");
  154.         patterns.setProperty("restartedStory", "'{'\"story\": '{'\"outcome\": \"restartedStory\", \"value\": \"{0}\", \"reason\": \"{1}\"}}");
  155.         patterns.setProperty("outcomesTableStart", "'{'\"outcomes\": '{'");
  156.         patterns.setProperty("outcomesTableHeadStart", "\"fields\": [");
  157.         patterns.setProperty("outcomesTableHeadCell", "\"{0}\"");
  158.         patterns.setProperty("outcomesTableHeadEnd", "]");
  159.         patterns.setProperty("outcomesTableBodyStart", "\"values\": [");
  160.         patterns.setProperty("outcomesTableRowStart", "[");
  161.         patterns.setProperty("outcomesTableCell", "\"{0}\"");
  162.         patterns.setProperty("outcomesTableRowEnd", "]");
  163.         patterns.setProperty("outcomesTableBodyEnd", "]");
  164.         patterns.setProperty("outcomesTableEnd", "}}");
  165.         patterns.setProperty("beforeExampleParameters", " \"parameters\": '{'");
  166.         patterns.setProperty("afterExampleParameters", "}");
  167.         patterns.setProperty("exampleParameter", "\"{0}\":\"{1}\"");
  168.         patterns.setProperty("numericParameter", "\"{0}\":{1}");
  169.         patterns.setProperty("beforeExamples", "\"examples\": '{'\"keyword\": \"{0}\"");
  170.         patterns.setProperty("examplesStepsStart", "\"steps\": [");
  171.         patterns.setProperty("examplesStep", "\"{0}\"");
  172.         patterns.setProperty("examplesStepsEnd", "]");
  173.         patterns.setProperty("afterExamples", "}");
  174.         patterns.setProperty("examplesTableStart", "\"parameters\": '{'");
  175.         patterns.setProperty("examplesTableHeadStart", "\"names\": [");
  176.         patterns.setProperty("examplesTableHeadCell", "\"{0}\"");
  177.         patterns.setProperty("examplesTableHeadEnd", "]");
  178.         patterns.setProperty("examplesTableBodyStart", "\"values\": [");
  179.         patterns.setProperty("examplesTableRowStart", "[");
  180.         patterns.setProperty("examplesTableCell", "\"{0}\"");
  181.         patterns.setProperty("examplesTableRowEnd", "]");
  182.         patterns.setProperty("examplesTableBodyEnd", "]");
  183.         patterns.setProperty("examplesTableEnd", "}");
  184.         patterns.setProperty("exampleScenariosStart", "\"examples\": [");
  185.         patterns.setProperty("exampleScenariosEnd", "]");
  186.         patterns.setProperty("example", "'{'\"keyword\": \"{0}\"");
  187.         patterns.setProperty("parameterVerbatimStart", "[[");
  188.         patterns.setProperty("parameterVerbatimEnd", "]]");
  189.         patterns.setProperty("parameterValueStart", "((");
  190.         patterns.setProperty("parameterValueEnd", "))");
  191.         patterns.setProperty("parameterValueNewline", "\\n");
  192.         patterns.setProperty("beforeComposedSteps", "\"steps\": [");
  193.         patterns.setProperty("afterComposedSteps", "]");
  194.         return patterns;
  195.     }
  196. }