Coverage Report - org.jbehave.core.reporters.TemplateableOutput
 
Classes in this File Line Coverage Branch Coverage Complexity
TemplateableOutput
81%
77/94
38%
7/18
1.336
TemplateableOutput$OutputKeywords
54%
18/33
N/A
1.336
TemplateableOutput$OutputLifecycle
0%
0/5
N/A
1.336
TemplateableOutput$OutputMeta
100%
8/8
100%
2/2
1.336
TemplateableOutput$OutputNarrative
87%
7/8
N/A
1.336
TemplateableOutput$OutputParameter
100%
7/7
N/A
1.336
TemplateableOutput$OutputRestart
100%
2/2
N/A
1.336
TemplateableOutput$OutputScenario
91%
22/24
83%
5/6
1.336
TemplateableOutput$OutputStep
78%
45/57
66%
16/24
1.336
TemplateableOutput$OutputStep$StepFormattingFailed
0%
0/2
N/A
1.336
TemplateableOutput$OutputStory
91%
11/12
N/A
1.336
 
 1  
 package org.jbehave.core.reporters;
 2  
 
 3  
 import java.io.File;
 4  
 import java.io.FileWriter;
 5  
 import java.io.Writer;
 6  
 import java.text.MessageFormat;
 7  
 import java.util.ArrayList;
 8  
 import java.util.HashMap;
 9  
 import java.util.List;
 10  
 import java.util.Map;
 11  
 import java.util.regex.Matcher;
 12  
 import java.util.regex.Pattern;
 13  
 
 14  
 import org.apache.commons.lang.StringEscapeUtils;
 15  
 import org.apache.commons.lang.StringUtils;
 16  
 import org.jbehave.core.configuration.Keywords;
 17  
 import org.jbehave.core.model.ExamplesTable;
 18  
 import org.jbehave.core.model.GivenStories;
 19  
 import org.jbehave.core.model.Lifecycle;
 20  
 import org.jbehave.core.model.Meta;
 21  
 import org.jbehave.core.model.Narrative;
 22  
 import org.jbehave.core.model.OutcomesTable;
 23  
 import org.jbehave.core.model.Scenario;
 24  
 import org.jbehave.core.model.Story;
 25  
 import org.jbehave.core.model.StoryDuration;
 26  
 
 27  
 import freemarker.ext.beans.BeansWrapper;
 28  
 import freemarker.template.TemplateHashModel;
 29  
 import freemarker.template.TemplateModelException;
 30  
 
 31  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_TABLE_END;
 32  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_TABLE_START;
 33  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_END;
 34  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_START;
 35  
 
 36  
 /**
 37  
  * <p>
 38  
  * Story reporter that outputs to a template.
 39  
  * </p>
 40  
  */
 41  
 public class TemplateableOutput implements StoryReporter {
 42  
 
 43  
     private final File file;
 44  
     private final Keywords keywords;
 45  
     private final TemplateProcessor processor;
 46  
     private final String templatePath;
 47  2
     private OutputStory outputStory = new OutputStory();
 48  2
     private OutputScenario outputScenario = new OutputScenario();
 49  
     private OutputStep failedStep;
 50  
 
 51  2
     public TemplateableOutput(File file, Keywords keywords, TemplateProcessor processor, String templatePath) {
 52  2
         this.file = file;
 53  2
         this.keywords = keywords;
 54  2
         this.processor = processor;
 55  2
         this.templatePath = templatePath;
 56  2
     }
 57  
 
 58  
     public void storyNotAllowed(Story story, String filter) {
 59  0
         this.outputStory.notAllowedBy = filter;
 60  0
     }
 61  
 
 62  
     public void beforeStory(Story story, boolean givenStory) {
 63  2
         if (!givenStory) {
 64  2
             this.outputStory = new OutputStory();
 65  2
             this.outputStory.description = story.getDescription().asString();
 66  2
             this.outputStory.path = story.getPath();
 67  
         }
 68  2
         if (!story.getMeta().isEmpty()) {
 69  2
             this.outputStory.meta = new OutputMeta(story.getMeta());
 70  
         }
 71  2
     }
 72  
 
 73  
     public void narrative(Narrative narrative) {
 74  2
         if (!narrative.isEmpty()) {
 75  2
             this.outputStory.narrative = new OutputNarrative(narrative);
 76  
         }
 77  2
     }
 78  
 
 79  
     public void lifecyle(Lifecycle lifecycle) {
 80  0
         if(!lifecycle.isEmpty()){
 81  0
             this.outputStory.lifecycle = new OutputLifecycle(lifecycle);            
 82  
         }
 83  0
     }
 84  
 
 85  
 
 86  
     public void scenarioNotAllowed(Scenario scenario, String filter) {
 87  0
         this.outputScenario.notAllowedBy = filter;
 88  0
     }
 89  
 
 90  
     public void beforeScenario(String title) {
 91  4
         if (this.outputScenario.currentExample == null) {
 92  4
             this.outputScenario = new OutputScenario();
 93  
         }
 94  4
         this.outputScenario.title = title;
 95  4
     }
 96  
 
 97  
     public void beforeStep(String step) {
 98  0
     }
 99  
 
 100  
     public void successful(String step) {
 101  22
         this.outputScenario.addStep(new OutputStep(step, "successful"));
 102  22
     }
 103  
 
 104  
     public void ignorable(String step) {
 105  2
         this.outputScenario.addStep(new OutputStep(step, "ignorable"));
 106  2
     }
 107  
 
 108  
     public void pending(String step) {
 109  0
         this.outputScenario.addStep(new OutputStep(step, "pending"));
 110  0
     }
 111  
 
 112  
     public void notPerformed(String step) {
 113  2
         this.outputScenario.addStep(new OutputStep(step, "notPerformed"));
 114  2
     }
 115  
 
 116  
     public void failed(String step, Throwable storyFailure) {
 117  6
         this.failedStep = new OutputStep(step, "failed");
 118  6
         failedStep.failure = storyFailure;
 119  6
         this.outputScenario.addStep(failedStep);
 120  6
     }
 121  
 
 122  
     public void failedOutcomes(String step, OutcomesTable table) {
 123  2
         failed(step, table.failureCause());
 124  2
         this.failedStep.outcomes = table;
 125  2
     }
 126  
 
 127  
     public void givenStories(GivenStories givenStories) {
 128  2
         if (!givenStories.getStories().isEmpty()) {
 129  2
             this.outputScenario.givenStories = givenStories;
 130  
         }
 131  2
     }
 132  
 
 133  
     public void givenStories(List<String> storyPaths) {
 134  2
         givenStories(new GivenStories(StringUtils.join(storyPaths, ",")));
 135  2
     }
 136  
 
 137  
     public void scenarioMeta(Meta meta) {
 138  0
         if (!meta.isEmpty()) {
 139  0
             this.outputScenario.meta = new OutputMeta(meta);
 140  
         }
 141  0
     }
 142  
 
 143  
     public void beforeExamples(List<String> steps, ExamplesTable table) {
 144  2
         this.outputScenario.examplesSteps = steps;
 145  2
         this.outputScenario.examplesTable = table;
 146  2
     }
 147  
 
 148  
     public void example(Map<String, String> parameters) {
 149  4
         this.outputScenario.examples.add(parameters);
 150  4
         this.outputScenario.currentExample = parameters;
 151  4
     }
 152  
 
 153  
     public void afterExamples() {
 154  2
         this.outputScenario.currentExample = null;
 155  2
     }
 156  
 
 157  
     public void dryRun() {
 158  2
     }
 159  
 
 160  
     public void afterScenario() {
 161  4
         if (this.outputScenario.currentExample == null) {
 162  4
             this.outputStory.scenarios.add(outputScenario);
 163  
         }
 164  4
     }
 165  
 
 166  
     public void pendingMethods(List<String> methods) {
 167  2
         this.outputStory.pendingMethods = methods;
 168  2
     }
 169  
 
 170  
     public void restarted(String step, Throwable cause) {
 171  2
         this.outputScenario.addStep(new OutputRestart(step, cause.getMessage()));
 172  2
     }
 173  
 
 174  
     public void storyCancelled(Story story, StoryDuration storyDuration) {
 175  2
         this.outputStory.cancelled = true;
 176  2
         this.outputStory.storyDuration = storyDuration;
 177  2
     }
 178  
 
 179  
     public void afterStory(boolean givenStory) {
 180  2
         if (!givenStory) {
 181  2
             Map<String, Object> model = newDataModel();
 182  2
             model.put("story", outputStory);
 183  2
             model.put("keywords", new OutputKeywords(keywords));
 184  
 
 185  2
             BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
 186  2
             TemplateHashModel enumModels = wrapper.getEnumModels();
 187  
             TemplateHashModel escapeEnums;
 188  
             try {
 189  2
                 String escapeModeEnum = EscapeMode.class.getCanonicalName();
 190  2
                 escapeEnums = (TemplateHashModel) enumModels.get(escapeModeEnum);
 191  2
                 model.put("EscapeMode", escapeEnums);  
 192  0
             } catch (TemplateModelException e) {
 193  0
                 throw new IllegalArgumentException(e);
 194  2
             }  
 195  
 
 196  2
             write(file, templatePath, model);
 197  
         }
 198  2
     }
 199  
 
 200  
     private File write(File file, String resource, Map<String, Object> dataModel) {
 201  
         try {
 202  2
             file.getParentFile().mkdirs();
 203  2
             Writer writer = new FileWriter(file);
 204  2
             processor.process(resource, dataModel, writer);
 205  2
             writer.close();
 206  2
             return file;
 207  0
         } catch (Exception e) {
 208  0
             throw new RuntimeException(resource, e);
 209  
         }
 210  
     }
 211  
 
 212  
     private Map<String, Object> newDataModel() {
 213  2
         return new HashMap<String, Object>();
 214  
     }
 215  
 
 216  
     public static class OutputKeywords {
 217  
 
 218  
         private final Keywords keywords;
 219  
 
 220  2
         public OutputKeywords(Keywords keywords) {
 221  2
             this.keywords = keywords;
 222  2
         }
 223  
 
 224  
         public String getLifecycle(){
 225  0
             return keywords.lifecycle();
 226  
         }
 227  
 
 228  
         public String getBefore(){
 229  0
             return keywords.before();
 230  
         }
 231  
 
 232  
         public String getAfter(){
 233  0
             return keywords.after();
 234  
         }
 235  
 
 236  
         public String getMeta() {
 237  1
             return keywords.meta();
 238  
         }
 239  
 
 240  
         public String getMetaProperty() {
 241  4
             return keywords.metaProperty();
 242  
         }
 243  
 
 244  
         public String getNarrative() {
 245  2
             return keywords.narrative();
 246  
         }
 247  
 
 248  
         public String getInOrderTo() {
 249  2
             return keywords.inOrderTo();
 250  
         }
 251  
 
 252  
         public String getAsA() {
 253  2
             return keywords.asA();
 254  
         }
 255  
 
 256  
         public String getiWantTo() {
 257  2
             return keywords.iWantTo();
 258  
         }
 259  
 
 260  
         public String getSoThat() {
 261  0
             return keywords.soThat();
 262  
         }
 263  
 
 264  
         public String getScenario() {
 265  4
             return keywords.scenario();
 266  
         }
 267  
 
 268  
         public String getGivenStories() {
 269  2
             return keywords.givenStories();
 270  
         }
 271  
 
 272  
         public String getExamplesTable() {
 273  2
             return keywords.examplesTable();
 274  
         }
 275  
 
 276  
         public String getExamplesTableRow() {
 277  4
             return keywords.examplesTableRow();
 278  
         }
 279  
 
 280  
         public String getExamplesTableHeaderSeparator() {
 281  0
             return keywords.examplesTableHeaderSeparator();
 282  
         }
 283  
 
 284  
         public String getExamplesTableValueSeparator() {
 285  0
             return keywords.examplesTableValueSeparator();
 286  
         }
 287  
 
 288  
         public String getExamplesTableIgnorableSeparator() {
 289  0
             return keywords.examplesTableIgnorableSeparator();
 290  
         }
 291  
 
 292  
         public String getGiven() {
 293  0
             return keywords.given();
 294  
         }
 295  
 
 296  
         public String getWhen() {
 297  0
             return keywords.when();
 298  
         }
 299  
 
 300  
         public String getThen() {
 301  0
             return keywords.then();
 302  
         }
 303  
 
 304  
         public String getAnd() {
 305  0
             return keywords.and();
 306  
         }
 307  
 
 308  
         public String getIgnorable() {
 309  0
             return keywords.ignorable();
 310  
         }
 311  
 
 312  
         public String getPending() {
 313  0
             return keywords.pending();
 314  
         }
 315  
 
 316  
         public String getNotPerformed() {
 317  1
             return keywords.notPerformed();
 318  
         }
 319  
 
 320  
         public String getFailed() {
 321  3
             return keywords.failed();
 322  
         }
 323  
 
 324  
         public String getDryRun() {
 325  0
             return keywords.dryRun();
 326  
         }
 327  
 
 328  
         public String getStoryCancelled() {
 329  2
             return keywords.storyCancelled();
 330  
         }
 331  
 
 332  
         public String getDuration() {
 333  2
             return keywords.duration();
 334  
         }
 335  
 
 336  
         public String getYes() {
 337  0
             return keywords.yes();
 338  
         }
 339  
 
 340  
         public String getNo() {
 341  4
             return keywords.no();
 342  
         }
 343  
     }
 344  
 
 345  22
     public static class OutputStory {
 346  
         private String description;
 347  
         private String path;
 348  
         private OutputMeta meta;
 349  
         private OutputNarrative narrative;
 350  
         private OutputLifecycle lifecycle;
 351  
         private String notAllowedBy;
 352  
         private List<String> pendingMethods;
 353  4
         private List<OutputScenario> scenarios = new ArrayList<OutputScenario>();
 354  
         private boolean cancelled;
 355  
         private StoryDuration storyDuration;
 356  
 
 357  
         public String getDescription() {
 358  2
             return description;
 359  
         }
 360  
 
 361  
         public String getPath() {
 362  2
             return path;
 363  
         }
 364  
 
 365  
         public OutputMeta getMeta() {
 366  4
             return meta;
 367  
         }
 368  
 
 369  
         public OutputNarrative getNarrative() {
 370  4
             return narrative;
 371  
         }
 372  
 
 373  
         public OutputLifecycle getLifecycle() {
 374  2
             return lifecycle;
 375  
         }
 376  
 
 377  
         public String getNotAllowedBy() {
 378  0
             return notAllowedBy;
 379  
         }
 380  
 
 381  
         public List<String> getPendingMethods() {
 382  4
             return pendingMethods;
 383  
         }
 384  
 
 385  
         public List<OutputScenario> getScenarios() {
 386  2
             return scenarios;
 387  
         }
 388  
 
 389  
         public boolean isCancelled() {
 390  2
             return cancelled;
 391  
         }
 392  
 
 393  
         public StoryDuration getStoryDuration() {
 394  2
             return storyDuration;
 395  
         }
 396  
     }
 397  
 
 398  
     public static class OutputMeta {
 399  
 
 400  
         private final Meta meta;
 401  
 
 402  2
         public OutputMeta(Meta meta) {
 403  2
             this.meta = meta;
 404  2
         }
 405  
 
 406  
         public Map<String, String> getProperties() {
 407  2
             Map<String, String> properties = new HashMap<String, String>();
 408  2
             for (String name : meta.getPropertyNames()) {
 409  4
                 properties.put(name, meta.getProperty(name));
 410  4
             }
 411  2
             return properties;
 412  
         }
 413  
 
 414  
     }
 415  
 
 416  
     public static class OutputNarrative {
 417  
         private final Narrative narrative;
 418  
 
 419  2
         public OutputNarrative(Narrative narrative) {
 420  2
             this.narrative = narrative;
 421  2
         }
 422  
 
 423  
         public String getInOrderTo() {
 424  2
             return narrative.inOrderTo();
 425  
         }
 426  
 
 427  
         public String getAsA() {
 428  2
             return narrative.asA();
 429  
         }
 430  
 
 431  
         public String getiWantTo() {
 432  2
             return narrative.iWantTo();
 433  
         }
 434  
         
 435  
         public String getSoThat(){
 436  0
             return narrative.soThat();
 437  
         }
 438  
         
 439  
         public boolean isAlternative(){
 440  2
             return narrative.isAlternative();
 441  
         }
 442  
 
 443  
     }
 444  
 
 445  
     public static class OutputLifecycle {
 446  
         private final Lifecycle lifecycle;
 447  
 
 448  0
         public OutputLifecycle(Lifecycle lifecycle) {
 449  0
             this.lifecycle = lifecycle;
 450  0
         }
 451  
 
 452  
         public List<String> getBeforeSteps(){
 453  0
             return lifecycle.getBeforeSteps();
 454  
         }
 455  
 
 456  
         public List<String> getAfterSteps(){
 457  0
             return lifecycle.getAfterSteps();
 458  
         }
 459  
 
 460  
     }
 461  
 
 462  34
     public static class OutputScenario {
 463  
         private String title;
 464  6
         private List<OutputStep> steps = new ArrayList<OutputStep>();
 465  
         private OutputMeta meta;
 466  
         private GivenStories givenStories;
 467  
         private String notAllowedBy;
 468  
         private List<String> examplesSteps;
 469  
         private ExamplesTable examplesTable;
 470  
         private Map<String, String> currentExample;
 471  6
         private List<Map<String, String>> examples = new ArrayList<Map<String, String>>();
 472  6
         private Map<Map<String, String>, List<OutputStep>> stepsByExample = new HashMap<Map<String, String>, List<OutputStep>>();
 473  
 
 474  
         public String getTitle() {
 475  4
             return title;
 476  
         }
 477  
 
 478  
         public void addStep(OutputStep outputStep) {
 479  34
             if (examplesTable == null) {
 480  24
                 steps.add(outputStep);
 481  
             } else {
 482  10
                 List<OutputStep> currentExampleSteps = stepsByExample.get(currentExample);
 483  10
                 if (currentExampleSteps == null) {
 484  4
                     currentExampleSteps = new ArrayList<OutputStep>();
 485  4
                     stepsByExample.put(currentExample, currentExampleSteps);
 486  
                 }
 487  10
                 currentExampleSteps.add(outputStep);
 488  
             }
 489  34
         }
 490  
 
 491  
         public List<OutputStep> getSteps() {
 492  2
             return steps;
 493  
         }
 494  
 
 495  
         public List<OutputStep> getStepsByExample(Map<String, String> example) {
 496  4
             List<OutputStep> steps = stepsByExample.get(example);
 497  4
             if (steps == null) {
 498  0
                 return new ArrayList<OutputStep>();
 499  
             }
 500  4
             return steps;
 501  
         }
 502  
 
 503  
         public OutputMeta getMeta() {
 504  4
             return meta;
 505  
         }
 506  
 
 507  
         public GivenStories getGivenStories() {
 508  6
             return givenStories;
 509  
         }
 510  
 
 511  
         public String getNotAllowedBy() {
 512  0
             return notAllowedBy;
 513  
         }
 514  
 
 515  
         public List<String> getExamplesSteps() {
 516  2
             return examplesSteps;
 517  
         }
 518  
 
 519  
         public ExamplesTable getExamplesTable() {
 520  6
             return examplesTable;
 521  
         }
 522  
 
 523  
         public List<Map<String, String>> getExamples() {
 524  4
             return examples;
 525  
         }
 526  
     }
 527  
 
 528  
     public static class OutputRestart extends OutputStep {
 529  
 
 530  
         public OutputRestart(String step, String outcome) {
 531  2
             super(step, outcome);
 532  2
         }
 533  
 
 534  
     }
 535  
 
 536  8
     public static class OutputStep {
 537  
         private final String step;
 538  
         private final String outcome;
 539  
         private Throwable failure;
 540  
         private OutcomesTable outcomes;
 541  
         private List<OutputParameter> parameters;
 542  
         private String stepPattern;
 543  
         private String tableAsString;
 544  
         private ExamplesTable table;
 545  
 
 546  34
         public OutputStep(String step, String outcome) {
 547  34
             this.step = step;
 548  34
             this.outcome = outcome;
 549  34
             parseTableAsString();
 550  34
             parseParameters();
 551  34
             createStepPattern();
 552  34
         }
 553  
 
 554  
         public String getStep() {
 555  0
             return step;
 556  
         }
 557  
 
 558  
         public String getStepPattern() {
 559  0
             return stepPattern;
 560  
         }
 561  
 
 562  
         public List<OutputParameter> getParameters() {
 563  0
             return parameters;
 564  
         }
 565  
 
 566  
         public String getOutcome() {
 567  51
             return outcome;
 568  
         }
 569  
 
 570  
         public Throwable getFailure() {
 571  34
             return failure;
 572  
         }
 573  
 
 574  
         public String getFailureCause() {
 575  6
             if (failure != null) {
 576  6
                 return new StackTraceFormatter(true).stackTrace(failure);
 577  
             }
 578  0
             return "";
 579  
         }
 580  
 
 581  
         public ExamplesTable getTable() {
 582  34
             return table;
 583  
         }
 584  
 
 585  
         public OutcomesTable getOutcomes() {
 586  36
             return outcomes;
 587  
         }
 588  
 
 589  
         public String getOutcomesFailureCause() {
 590  2
             if (outcomes.failureCause() != null) {
 591  2
                 return new StackTraceFormatter(true).stackTrace(outcomes.failureCause());
 592  
             }
 593  0
             return "";
 594  
         }
 595  
 
 596  
         /*
 597  
          * formatting without escaping doesn't make sense unless
 598  
          * we do a ftl text output format
 599  
          */
 600  
         @Deprecated
 601  
         public String getFormattedStep(String parameterPattern) {
 602  0
             return getFormattedStep(EscapeMode.NONE, parameterPattern);
 603  
         }
 604  
 
 605  
         public String getFormattedStep(EscapeMode outputFormat, String parameterPattern) {
 606  
             // note that escaping the stepPattern string only works
 607  
             // because placeholders for parameters do not contain
 608  
             // special chars (the placeholder is {0} etc)
 609  34
             String escapedStep = escapeString(outputFormat, stepPattern);
 610  34
             if (!parameters.isEmpty()) {
 611  
                 try {
 612  6
                     return MessageFormat.format(escapedStep, formatParameters(outputFormat, parameterPattern));
 613  0
                 } catch (RuntimeException e) {
 614  0
                     throw new StepFormattingFailed(stepPattern, parameterPattern, parameters, e);
 615  
                 }
 616  
             }
 617  28
             return escapedStep;
 618  
         }
 619  
 
 620  
         private String escapeString(EscapeMode outputFormat, String string) {
 621  42
             if(outputFormat==EscapeMode.HTML) {
 622  21
                 return StringEscapeUtils.escapeHtml(string);
 623  21
             } else if(outputFormat==EscapeMode.XML) {
 624  21
                 return StringEscapeUtils.escapeXml(string);
 625  
             } else {
 626  0
                 return string;
 627  
             }
 628  
         }
 629  
 
 630  
         private Object[] formatParameters(EscapeMode outputFormat, String parameterPattern) {
 631  6
             Object[] arguments = new Object[parameters.size()];
 632  14
             for (int a = 0; a < parameters.size(); a++) {
 633  8
                 arguments[a] = MessageFormat.format(parameterPattern, escapeString(outputFormat, parameters.get(a).getValue()));
 634  
             }
 635  6
             return arguments;
 636  
         }
 637  
 
 638  
         private void parseParameters() {
 639  
             // first, look for parameterized scenarios
 640  34
             parameters = findParameters(PARAMETER_VALUE_START + PARAMETER_VALUE_START, PARAMETER_VALUE_END
 641  
                     + PARAMETER_VALUE_END);
 642  
             // second, look for normal scenarios
 643  34
             if (parameters.isEmpty()) {
 644  34
                 parameters = findParameters(PARAMETER_VALUE_START, PARAMETER_VALUE_END);
 645  
             }
 646  34
         }
 647  
 
 648  
         private List<OutputParameter> findParameters(String start, String end) {
 649  68
             List<OutputParameter> parameters = new ArrayList<OutputParameter>();
 650  68
             Matcher matcher = Pattern.compile("(" + start + ".*?" + end + ")(\\W|\\Z)",
 651  
                     Pattern.DOTALL).matcher(step);
 652  76
             while (matcher.find()) {
 653  8
                 parameters.add(new OutputParameter(step, matcher.start(), matcher.end()));
 654  
             }
 655  68
             return parameters;
 656  
         }
 657  
 
 658  
         private void parseTableAsString() {
 659  34
             if (step.contains(PARAMETER_TABLE_START) && step.contains(PARAMETER_TABLE_END)) {
 660  0
                 tableAsString = StringUtils.substringBetween(step, PARAMETER_TABLE_START, PARAMETER_TABLE_END);
 661  0
                 table = new ExamplesTable(tableAsString);
 662  
             }
 663  34
         }
 664  
 
 665  
         private void createStepPattern() {
 666  34
             this.stepPattern = step;
 667  34
             if (tableAsString != null) {
 668  0
                 this.stepPattern = StringUtils.replaceOnce(stepPattern, PARAMETER_TABLE_START + tableAsString
 669  
                         + PARAMETER_TABLE_END, "");
 670  
             }
 671  42
             for (int count = 0; count < parameters.size(); count++) {
 672  8
                 String value = parameters.get(count).toString();
 673  8
                 this.stepPattern = stepPattern.replace(value, "{" + count + "}");
 674  
             }
 675  34
         }
 676  
 
 677  
         @SuppressWarnings("serial")
 678  
         public static class StepFormattingFailed extends RuntimeException {
 679  
 
 680  
             public StepFormattingFailed(String stepPattern, String parameterPattern, List<OutputParameter> parameters,
 681  
                     RuntimeException cause) {
 682  0
                 super("Failed to format step '" + stepPattern + "' with parameter pattern '" + parameterPattern
 683  
                         + "' and parameters: " + parameters, cause);
 684  0
             }
 685  
 
 686  
         }
 687  
 
 688  
     }
 689  
 
 690  
     public static class OutputParameter {
 691  
         private final String parameter;
 692  
 
 693  8
         public OutputParameter(String pattern, int start, int end) {
 694  8
             this.parameter = pattern.substring(start, end).trim();
 695  8
         }
 696  
 
 697  
         public String getValue() {
 698  8
             String value = StringUtils.remove(parameter, PARAMETER_VALUE_START);
 699  8
             value = StringUtils.remove(value, PARAMETER_VALUE_END);
 700  8
             return value;
 701  
         }
 702  
 
 703  
         @Override
 704  
         public String toString() {
 705  8
             return parameter;
 706  
         }
 707  
     }
 708  
 
 709  
 }