Coverage Report - org.jbehave.core.reporters.PrintStreamOutput
 
Classes in this File Line Coverage Branch Coverage Complexity
PrintStreamOutput
93%
151/162
77%
42/54
1.773
PrintStreamOutput$1
100%
6/6
80%
4/5
1.773
PrintStreamOutput$2
100%
1/1
N/A
1.773
PrintStreamOutput$Format
100%
1/1
N/A
1.773
PrintStreamOutput$Replacement
100%
5/5
N/A
1.773
 
 1  
 package org.jbehave.core.reporters;
 2  
 
 3  
 import java.io.ByteArrayOutputStream;
 4  
 import java.io.OutputStream;
 5  
 import java.io.PrintStream;
 6  
 import java.text.MessageFormat;
 7  
 import java.util.Arrays;
 8  
 import java.util.List;
 9  
 import java.util.Locale;
 10  
 import java.util.Map;
 11  
 import java.util.Properties;
 12  
 import java.util.regex.Pattern;
 13  
 
 14  
 import org.apache.commons.collections.CollectionUtils;
 15  
 import org.apache.commons.collections.Transformer;
 16  
 import org.apache.commons.lang.ArrayUtils;
 17  
 import org.apache.commons.lang.StringUtils;
 18  
 import org.apache.commons.lang.builder.ToStringBuilder;
 19  
 import org.apache.commons.lang.builder.ToStringStyle;
 20  
 import org.jbehave.core.configuration.Keywords;
 21  
 import org.jbehave.core.failures.KnownFailure;
 22  
 import org.jbehave.core.failures.UUIDExceptionWrapper;
 23  
 import org.jbehave.core.model.ExamplesTable;
 24  
 import org.jbehave.core.model.GivenStories;
 25  
 import org.jbehave.core.model.GivenStory;
 26  
 import org.jbehave.core.model.Meta;
 27  
 import org.jbehave.core.model.Narrative;
 28  
 import org.jbehave.core.model.OutcomesTable;
 29  
 import org.jbehave.core.model.OutcomesTable.Outcome;
 30  
 import org.jbehave.core.model.Scenario;
 31  
 import org.jbehave.core.model.Story;
 32  
 
 33  
 import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
 34  
 import static org.apache.commons.lang.StringEscapeUtils.escapeXml;
 35  
 import static org.apache.commons.lang.StringUtils.substringBetween;
 36  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_TABLE_END;
 37  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_TABLE_START;
 38  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_END;
 39  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_NEWLINE;
 40  
 import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_START;
 41  
 
 42  
 /**
 43  
  * <p>
 44  
  * Abstract story reporter that outputs to a PrintStream.
 45  
  * </p>
 46  
  * <p>
 47  
  * The output of the reported event is configurable via:
 48  
  * <ul>
 49  
  * <li>custom output patterns, providing only the patterns that differ from
 50  
  * default</li>
 51  
  * <li>keywords localised for different languages, providing the i18n Locale</li>
 52  
  * <li>flag to report failure trace</li>
 53  
  * </ul>
 54  
  * </p>
 55  
  * <p>
 56  
  * Let's look at example of providing custom output patterns, e.g. for the
 57  
  * failed event. <br/>
 58  
  * we'd need to provide the custom pattern, say we want to have something like
 59  
  * "(step being executed) <<< FAILED", keyed on the method name:
 60  
  * 
 61  
  * <pre>
 62  
  * Properties patterns = new Properties();
 63  
  * patterns.setProperty(&quot;failed&quot;, &quot;{0} &lt;&lt;&lt; {1}&quot;);
 64  
  * </pre>
 65  
  * 
 66  
  * The pattern is by default processed and formatted by the
 67  
  * {@link MessageFormat}. Both the {@link #format(String key, String defaultPattern, Object... args)} and
 68  
  * {@link #lookupPattern(String key, String defaultPattern)} methods are override-able and a different formatter
 69  
  * or pattern lookup can be used by subclasses.
 70  
  * </p>
 71  
  * <p>
 72  
  * If the keyword "FAILED" (or any other keyword used by the reporter) needs to
 73  
  * be expressed in a different language, all we need to do is to provide an
 74  
  * instance of {@link org.jbehave.core.i18n.LocalizedKeywords} using the appropriate {@link Locale}, e.g.
 75  
  * 
 76  
  * <pre>
 77  
  * Keywords keywords = new LocalizedKeywords(new Locale(&quot;it&quot;));
 78  
  * </pre>
 79  
  * 
 80  
  * </p>
 81  
  */
 82  
 public abstract class PrintStreamOutput implements StoryReporter {
 83  
 
 84  
     private static final String EMPTY = "";
 85  
 
 86  5
     public enum Format { TXT, HTML, XML }
 87  
     
 88  
     private final Format format;    
 89  
     private final PrintStream output;
 90  
     private final Properties outputPatterns;
 91  
     private final Keywords keywords;
 92  284
     private ThreadLocal<Boolean> reportFailureTrace = new ThreadLocal<Boolean>();
 93  284
     private ThreadLocal<Boolean> compressFailureTrace = new ThreadLocal<Boolean>();
 94  284
     private ThreadLocal<Throwable> cause = new ThreadLocal<Throwable>();
 95  
     
 96  
     protected PrintStreamOutput(Format format, PrintStream output, Properties outputPatterns,
 97  284
             Keywords keywords, boolean reportFailureTrace, boolean compressFailureTrace) {
 98  284
         this.format = format;
 99  284
         this.output = output;
 100  284
         this.outputPatterns = outputPatterns;
 101  284
         this.keywords = keywords;
 102  284
         doReportFailureTrace(reportFailureTrace);
 103  284
         doCompressFailureTrace(compressFailureTrace);   
 104  284
     }
 105  
 
 106  
     public void successful(String step) {
 107  44
         print(format("successful", "{0}\n", step));
 108  44
     }
 109  
 
 110  
     public void ignorable(String step) {
 111  12
         print(format("ignorable", "{0}\n", step));
 112  12
     }
 113  
 
 114  
     public void pending(String step) {
 115  16
         print(format("pending", "{0} ({1})\n", step, keywords.pending()));
 116  16
     }
 117  
 
 118  
     public void notPerformed(String step) {
 119  16
         print(format("notPerformed", "{0} ({1})\n", step, keywords.notPerformed()));
 120  16
     }
 121  
 
 122  
     public void failed(String step, Throwable storyFailure) {
 123  
         // storyFailure be used if a subclass has rewritten the "failed" pattern to have a {3} as WebDriverHtmlOutput (jbehave-web) does.
 124  19
         if (storyFailure instanceof UUIDExceptionWrapper) {
 125  19
             this.cause.set(storyFailure.getCause());
 126  19
             print(format("failed", "{0} ({1})\n({2})\n", step, keywords.failed(), storyFailure.getCause(), ((UUIDExceptionWrapper) storyFailure).getUUID()));
 127  
         } else {
 128  0
             throw new ClassCastException(storyFailure +" should be an instance of UUIDExceptionWrapper");
 129  
         }
 130  19
     }
 131  
 
 132  
     public void failedOutcomes(String step, OutcomesTable table) {
 133  12
             failed(step, table.failureCause());
 134  12
         print(table);
 135  12
     }
 136  
     
 137  
         private void print(OutcomesTable table) {
 138  12
                 print(format("outcomesTableStart", "\n"));
 139  12
         List<Outcome<?>> rows = table.getOutcomes();
 140  12
         print(format("outcomesTableHeadStart", "|"));
 141  
         //TODO i18n outcome fields
 142  12
         for (String field : table.getOutcomeFields()) {
 143  48
             print(format("outcomesTableHeadCell", "{0}|", field));
 144  
         }
 145  12
         print(format("outcomesTableHeadEnd", "\n"));
 146  12
         print(format("outcomesTableBodyStart", EMPTY));
 147  12
         for (Outcome<?> outcome : rows) {
 148  12
             print(format("outcomesTableRowStart", "|", outcome.isVerified()?"verified":"notVerified"));
 149  12
             print(format("outcomesTableCell", "{0}|", outcome.getDescription()));
 150  12
             print(format("outcomesTableCell", "{0}|", outcome.getValue()));
 151  12
             print(format("outcomesTableCell", "{0}|", outcome.getMatcher()));
 152  12
             print(format("outcomesTableCell", "{0}|", outcome.isVerified()));
 153  12
             print(format("outcomesTableRowEnd", "\n"));
 154  
         }
 155  12
         print(format("outcomesTableBodyEnd", "\n"));
 156  12
         print(format("outcomesTableEnd", "\n"));
 157  12
         }
 158  
 
 159  
     public void storyNotAllowed(Story story, String filter) {
 160  3
         print(format("filter", "{0}\n", filter));
 161  3
     }
 162  
 
 163  
     public void beforeStory(Story story, boolean givenStory) {
 164  15
         print(format("beforeStory", "{0}\n({1})\n", story.getDescription().asString(), story.getPath()));
 165  15
         if (!story.getMeta().isEmpty()) {
 166  15
             Meta meta = story.getMeta();
 167  15
             print(meta);
 168  
         }
 169  15
     }
 170  
 
 171  
     public void narrative(Narrative narrative) {
 172  12
         if (!narrative.isEmpty()) {
 173  12
             print(format("narrative", "{0}\n{1} {2}\n{3} {4}\n{5} {6}\n", keywords.narrative(), keywords.inOrderTo(),
 174  
                     narrative.inOrderTo(), keywords.asA(), narrative.asA(), keywords.iWantTo(), narrative.iWantTo()));
 175  
         }
 176  12
     }
 177  
 
 178  
     private void print(Meta meta) {
 179  18
         print(format("metaStart", "{0}\n", keywords.meta()));
 180  18
         for (String name : meta.getPropertyNames() ){
 181  36
             print(format("metaProperty", "{0}{1} {2}", keywords.metaProperty(), name, meta.getProperty(name)));                
 182  
         }
 183  18
         print(format("metaEnd", "\n"));
 184  18
     }
 185  
 
 186  
     public void afterStory(boolean givenStory) {
 187  15
         print(format("afterStory", "\n"));
 188  15
     }
 189  
 
 190  
     public void givenStories(GivenStories givenStories) {
 191  12
         print(format("givenStoriesStart", "{0}\n", keywords.givenStories()));
 192  12
         for (GivenStory givenStory : givenStories.getStories()) {
 193  24
             print(format("givenStory", "{0} {1}\n", givenStory.asString(), (givenStory.hasAnchor() ? givenStory.getParameters() : "")));
 194  
         }
 195  12
         print(format("givenStoriesEnd", "\n"));
 196  12
     }
 197  
 
 198  
     public void givenStories(List<String> storyPaths) {
 199  12
         givenStories(new GivenStories(StringUtils.join(storyPaths, ",")));
 200  12
     }
 201  
 
 202  
     public void scenarioNotAllowed(Scenario scenario, String filter) {
 203  3
         print(format("filter", "{0}\n", filter));
 204  3
     }
 205  
 
 206  
     public void beforeScenario(String title) {
 207  17
         cause.set(null);
 208  17
         print(format("beforeScenario", "{0} {1}\n", keywords.scenario(), title));
 209  17
     }
 210  
 
 211  
     public void scenarioMeta(Meta meta) {
 212  3
         if (!meta.isEmpty()) {
 213  3
             print(meta);
 214  
         }
 215  3
     }
 216  
 
 217  
     public void afterScenario() {
 218  19
         if (cause.get() != null && reportFailureTrace.get() && !(cause.get() instanceof KnownFailure) ) {
 219  4
             print(format("afterScenarioWithFailure", "\n{0}\n", stackTrace(cause.get())));
 220  
         } else {
 221  15
             print(format("afterScenario", "\n"));
 222  
         }
 223  19
     }
 224  
 
 225  
     private String stackTrace(Throwable cause) {
 226  4
         if (cause.getClass().getName().equals(UUIDExceptionWrapper.class.getName())) {
 227  0
             cause = cause.getCause();
 228  
         }
 229  4
         ByteArrayOutputStream out = new ByteArrayOutputStream();        
 230  4
         cause.printStackTrace(new PrintStream(out));
 231  4
         return stackTrace(out.toString());
 232  
     }
 233  
 
 234  
     protected String stackTrace(String stackTrace) {
 235  5
         if ( !compressFailureTrace.get() ){
 236  4
             return stackTrace;
 237  
         }
 238  
         // don't print past certain parts of the stack.  Try them even though they may be redundant.
 239  1
         stackTrace = cutOff(stackTrace, "org.jbehave.core.embedder.");
 240  1
         stackTrace = cutOff(stackTrace, "org.junit.runners.");
 241  1
         stackTrace = cutOff(stackTrace, "org.apache.maven.surefire.");
 242  
 
 243  
         //System.out.println("=====before>" + stackTrace + "<==========");
 244  
 
 245  
         // replace whole series of lines with '\t(summary)'  The end-user will thank us.
 246  9
         for (Replacement replacement : REPLACEMENTS) {
 247  8
             stackTrace = replacement.from.matcher(stackTrace).replaceAll(replacement.to);
 248  
         }
 249  1
         return stackTrace;
 250  
     }
 251  
 
 252  
     private String cutOff(String stackTrace, String at) {
 253  3
         if (stackTrace.indexOf(at) > -1) {
 254  1
             int ix = stackTrace.indexOf(at);
 255  1
             ix = stackTrace.indexOf("\n", ix);
 256  1
             if (ix != -1) {
 257  0
                 stackTrace = stackTrace.substring(0,ix);
 258  
             }
 259  
         }
 260  3
         return stackTrace;
 261  
     }
 262  
 
 263  
     public void beforeExamples(List<String> steps, ExamplesTable table) {
 264  12
         print(format("beforeExamples", "{0}\n", keywords.examplesTable()));
 265  12
         for (String step : steps) {
 266  24
             print(format("examplesStep", "{0}\n", step));
 267  
         }
 268  12
         print(formatTable(table));
 269  12
     }
 270  
 
 271  
     public void example(Map<String, String> tableRow) {
 272  24
         print(format("example", "\n{0} {1}\n", keywords.examplesTableRow(), tableRow));
 273  24
     }
 274  
 
 275  
     public void afterExamples() {
 276  12
         print(format("afterExamples", "\n"));
 277  12
     }
 278  
 
 279  
         public void dryRun() {
 280  12
                 print(format("dryRun", "{0}\n", keywords.dryRun()));
 281  12
         }
 282  
         
 283  
 
 284  
     public void pendingMethods(List<String> methods) {
 285  12
         for (String method : methods) {
 286  24
             print(format("pendingMethod", "{0}\n", method));
 287  
         }        
 288  12
     }
 289  
 
 290  
     /**
 291  
      * Formats event output by key, usually equal to the method name.
 292  
      * 
 293  
      * @param key the event key
 294  
      * @param defaultPattern the default pattern to return if a custom pattern
 295  
      *            is not found
 296  
      * @param args the args used to format output
 297  
      * @return A formatted event output
 298  
      */
 299  
     protected String format(String key, String defaultPattern, Object... args) {
 300  5787
         return MessageFormat.format(lookupPattern(key, escape(defaultPattern)), escapeAll(args));
 301  
     }
 302  
     
 303  
     protected String formatTable(ExamplesTable table) {
 304  12
         OutputStream formatted = new ByteArrayOutputStream();
 305  12
         PrintStream out = new PrintStream(formatted);
 306  12
         out.print(format("examplesTableStart", "\n"));
 307  12
         List<Map<String, String>> rows = table.getRows();
 308  12
         List<String> headers = table.getHeaders();
 309  12
         out.print(format("examplesTableHeadStart", "|"));
 310  12
         for (String header : headers) {
 311  24
             out.print(format("examplesTableHeadCell", "{0}|", header));
 312  
         }
 313  12
         out.print(format("examplesTableHeadEnd", "\n"));
 314  12
         out.print(format("examplesTableBodyStart", EMPTY));
 315  12
         for (Map<String, String> row : rows) {
 316  24
             out.print(format("examplesTableRowStart", "|"));
 317  24
             for (String header : headers) {
 318  48
                 out.print(format("examplesTableCell", "{0}|", row.get(header)));
 319  
             }
 320  24
             out.print(format("examplesTableRowEnd", "\n"));
 321  
         }
 322  12
         out.print(format("examplesTableBodyEnd", ""));
 323  12
         out.print(format("examplesTableEnd", ""));
 324  12
         return formatted.toString();
 325  
     }
 326  
 
 327  
     private String escape(String defaultPattern) {
 328  5787
         return (String) escapeAll(defaultPattern)[0];
 329  
     }
 330  
 
 331  
     private Object[] escapeAll(Object... args) {
 332  11574
         return escape(format, args);
 333  
     }
 334  
 
 335  
     /**
 336  
      * Escapes args' string values according to format
 337  
      * 
 338  
      * @param format the Format used by the PrintStream
 339  
      * @param args the array of args to escape
 340  
      * @return The cloned and escaped array of args
 341  
      */
 342  
     protected Object[] escape(final Format format, Object... args) {
 343  
         // Transformer that escapes HTML and XML strings
 344  11574
         Transformer escapingTransformer = new Transformer( ) {
 345  
             public Object transform(Object object) {
 346  6627
                 switch ( format ){
 347  2102
                     case HTML: return escapeHtml(asString(object));
 348  635
                     case XML: return escapeXml(asString(object));
 349  3890
                     default: return object;
 350  
                 }
 351  
             }
 352  
 
 353  
             private String asString(Object object) {
 354  2737
                 return  ( object != null ? object.toString() : EMPTY );
 355  
             }
 356  
         };
 357  11574
         List<?> list = Arrays.asList( ArrayUtils.clone( args ) );
 358  11574
         CollectionUtils.transform( list, escapingTransformer );
 359  11574
         return list.toArray();
 360  
     }
 361  
 
 362  
     /**
 363  
      * Looks up the format pattern for the event output by key, conventionally
 364  
      * equal to the method name. The pattern is used by the
 365  
      * {#format(String,String,Object...)} method and by default is formatted
 366  
      * using the {@link MessageFormat#format(String, Object...)} method. If no pattern is found
 367  
      * for key or needs to be overridden, the default pattern should be
 368  
      * returned.
 369  
      * 
 370  
      * @param key the format pattern key
 371  
      * @param defaultPattern the default pattern if no pattern is
 372  
      * @return The format patter for the given key
 373  
      */
 374  
     protected String lookupPattern(String key, String defaultPattern) {
 375  5787
         if (outputPatterns.containsKey(key)) {
 376  2658
             return outputPatterns.getProperty(key);
 377  
         }
 378  3129
         return defaultPattern;
 379  
     }
 380  
 
 381  
     public boolean reportFailureTrace(){
 382  1
         return reportFailureTrace.get();
 383  
     }
 384  
     
 385  
     public PrintStreamOutput doReportFailureTrace(boolean reportFailureTrace){
 386  302
             this.reportFailureTrace.set(reportFailureTrace);
 387  302
             return this;
 388  
     }
 389  
 
 390  
     public boolean compressFailureTrace(){
 391  1
         return compressFailureTrace.get();
 392  
     }
 393  
     
 394  
     public PrintStreamOutput doCompressFailureTrace(boolean compressFailureTrace){
 395  302
         this.compressFailureTrace.set(compressFailureTrace);
 396  302
         return this;
 397  
     }
 398  
 
 399  
     protected void overwritePattern(String key, String pattern) {
 400  0
         outputPatterns.put(key, pattern);
 401  0
     }
 402  
 
 403  
     /**
 404  
      * Prints text to output stream, replacing parameter start and end placeholders
 405  
      * 
 406  
      * @param text the String to print
 407  
      */
 408  
     protected void print(String text) {
 409  623
         if ( containsTable(text) ){
 410  0
             String tableStart = format(PARAMETER_TABLE_START, PARAMETER_TABLE_START);
 411  0
             String tableEnd = format(PARAMETER_TABLE_END, PARAMETER_TABLE_END);
 412  0
             String tableAsString = substringBetween(text, tableStart, tableEnd);
 413  0
             output.print(text
 414  
                     .replace(tableAsString, formatTable(new ExamplesTable(tableAsString)))
 415  
                     .replace(tableStart, format("parameterValueStart", EMPTY))
 416  
                     .replace(tableEnd, format("parameterValueEnd", EMPTY))
 417  
                     .replace(format(PARAMETER_VALUE_NEWLINE, PARAMETER_VALUE_NEWLINE),
 418  
                             format("parameterValueNewline", "\n")));
 419  0
         } else {
 420  623
             output.print(text
 421  
                     .replace(format(PARAMETER_VALUE_START, PARAMETER_VALUE_START), format("parameterValueStart", EMPTY))
 422  
                     .replace(format(PARAMETER_VALUE_END, PARAMETER_VALUE_END), format("parameterValueEnd", EMPTY))
 423  
                     .replace(format(PARAMETER_VALUE_NEWLINE, PARAMETER_VALUE_NEWLINE),
 424  
                             format("parameterValueNewline", "\n")));
 425  
         }
 426  623
     }
 427  
 
 428  
     private boolean containsTable(String text) {
 429  623
         String tableStart = format(PARAMETER_TABLE_START, PARAMETER_TABLE_START);
 430  623
         String tableEnd = format(PARAMETER_TABLE_END, PARAMETER_TABLE_END);
 431  623
         return text.contains(tableStart) && text.contains(tableEnd);
 432  
     }
 433  
 
 434  
     @Override
 435  
         public String toString() {
 436  0
                 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append(format).append(output).toString();
 437  
         }
 438  
 
 439  24
     private static class Replacement {
 440  
         private final Pattern from;
 441  
         private final String to;
 442  8
         private Replacement(Pattern from, String to) {
 443  8
             this.from = from;
 444  8
             this.to = to;
 445  8
         }
 446  
     }
 447  
 
 448  1
     private static Replacement[] REPLACEMENTS = new Replacement[]{
 449  
             new Replacement(
 450  
                     Pattern.compile(
 451  
                             "\\tat sun.reflect.NativeMethodAccessorImpl.invoke0\\(Native Method\\)\\n" +
 452  
                             "\\tat sun.reflect.NativeMethodAccessorImpl.invoke\\(NativeMethodAccessorImpl.java:\\d+\\)\\n" +
 453  
                             "\\tat sun.reflect.DelegatingMethodAccessorImpl.invoke\\(DelegatingMethodAccessorImpl.java:\\d+\\)\\n" +
 454  
                             "\\tat java.lang.reflect.Method.invoke\\(Method.java:\\d+\\)"
 455  
                     ),
 456  
                     "\t(reflection-invoke)"),
 457  
             new Replacement(
 458  
                     Pattern.compile(
 459  
                             "\\tat org.codehaus.groovy.reflection.CachedMethod.invoke\\(CachedMethod.java:\\d+\\)\\n" +
 460  
                             "\\tat org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke\\(ClosureMetaMethod.java:\\d+\\)\\n" +
 461  
                             "\\tat org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite\\$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke\\(PojoMetaMethodSite.java:\\d+\\)\\n" +
 462  
                             "\\tat org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call\\(PojoMetaMethodSite.java:\\d+\\)\\n" +
 463  
                             "\\tat org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall\\(CallSiteArray.java:\\d+\\)\\n" +
 464  
                             "\\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call\\(AbstractCallSite.java:\\d+\\)\\n" +
 465  
                             "\\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call\\(AbstractCallSite.java:\\d+\\)"
 466  
                     ),
 467  
                     "\t(groovy-closure-invoke)"),
 468  
             new Replacement(
 469  
                     Pattern.compile(
 470  
                             "\\tat org.codehaus.groovy.reflection.CachedMethod.invoke\\(CachedMethod.java:\\d+\\)\\n" +
 471  
                             "\\tat groovy.lang.MetaMethod.doMethodInvoke\\(MetaMethod.java:\\d+\\)\\n" +
 472  
                             "\\tat org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod\\(ClosureMetaClass.java:\\d+\\)\\n" +
 473  
                             "\\tat org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN\\(ScriptBytecodeAdapter.java:\\d+\\)"
 474  
                     ),
 475  
                     "\t(groovy-instance-method-invoke)"),
 476  
             new Replacement(
 477  
                     Pattern.compile(
 478  
                             "\\tat org.codehaus.groovy.reflection.CachedMethod.invoke\\(CachedMethod.java:\\d+\\)\n" +
 479  
                             "\\tat org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke\\(ClosureMetaMethod.java:\\d+\\)\n" +
 480  
                             "\\tat org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite\\$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke\\(PojoMetaMethodSite.java:\\d+\\)\n" +
 481  
                             "\\tat org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call\\(PojoMetaMethodSite.java:\\d+\\)\n" +
 482  
                             "\\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call\\(AbstractCallSite.java:\\d+\\)"
 483  
                     ),
 484  
                     "\t(groovy-abstract-method-invoke)"),
 485  
             new Replacement(
 486  
                     Pattern.compile(
 487  
                             "\\tat org.codehaus.groovy.reflection.CachedMethod.invoke\\(CachedMethod.java:\\d+\\)\\n" +
 488  
                             "\\tat groovy.lang.MetaMethod.doMethodInvoke\\(MetaMethod.java:\\d+\\)\\n" +
 489  
                             "\\tat groovy.lang.MetaClassImpl.invokeStaticMethod\\(MetaClassImpl.java:\\d+\\)\\n" +
 490  
                             "\\tat org.codehaus.groovy.runtime.InvokerHelper.invokeStaticMethod\\(InvokerHelper.java:\\d+\\)\\n" +
 491  
                             "\\tat org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeStaticMethodN\\(ScriptBytecodeAdapter.java:\\d+\\)"
 492  
                     ),
 493  
                     "\t(groovy-static-method-invoke)"),
 494  
             new Replacement(
 495  
                     Pattern.compile(
 496  
                             "\\tat sun.reflect.NativeConstructorAccessorImpl.newInstance0\\(Native Method\\)\\n" +
 497  
                             "\\tat sun.reflect.NativeConstructorAccessorImpl.newInstance\\(NativeConstructorAccessorImpl.java:\\d+\\)\\n" +
 498  
                             "\\tat sun.reflect.DelegatingConstructorAccessorImpl.newInstance\\(DelegatingConstructorAccessorImpl.java:\\d+\\)\\n" +
 499  
                             "\\tat java.lang.reflect.Constructor.newInstance\\(Constructor.java:\\d+\\)"
 500  
                     ),
 501  
                     "\t(reflection-construct)"),
 502  
             new Replacement(
 503  
                     Pattern.compile(
 504  
                             "\\tat org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(Current|)\\(CallSiteArray.java:\\d+\\)\\n" +
 505  
                             "\\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(Current|)\\(AbstractCallSite.java:\\d+\\)\\n" +
 506  
                             "\\tat org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(Current|)\\(AbstractCallSite.java:\\d+\\)"
 507  
 
 508  
                     ),
 509  
                     "\t(groovy-call)"),
 510  
             // This one last.
 511  
             new Replacement(
 512  
                     Pattern.compile(
 513  
                             "\\t\\(reflection\\-invoke\\)\\n" +
 514  
                                     "\\t\\(groovy\\-"),
 515  
                     "\t(groovy-")
 516  
     };
 517  
 
 518  
 
 519  
 
 520  
 }