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