KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > test > TestUtil


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb.test;
33
34 import java.io.File JavaDoc;
35 import java.io.FileReader JavaDoc;
36 import java.io.LineNumberReader JavaDoc;
37 import java.sql.Connection JavaDoc;
38 import java.sql.ResultSet JavaDoc;
39 import java.sql.SQLException JavaDoc;
40 import java.sql.Statement JavaDoc;
41
42 import org.hsqldb.lib.HsqlArrayList;
43 import org.hsqldb.lib.StringUtil;
44
45 /**
46  * Utility class providing methodes for submitting test statements or
47  * scripts to the database, comparing the results returned with
48  * the expected results. The test script format is compatible with existing
49  * scripts.
50  *
51  * @author ewanslater@users
52  * @author fredt@users
53  */

54 public class TestUtil {
55
56     /**
57      * Runs a preformatted script.<p>
58      *
59      * Where a result set is required, each line in the script will
60      * be interpreted as a seperate expected row in the ResultSet
61      * returned by the query. Within each row, fields should be delimited
62      * using either comma (the default), or a user defined delimiter
63      * which should be specified in the System property TestUtilFieldDelimiter
64      * @param aConnection Connection object for the database
65      * @param aPath Path of the script file to be tested
66      */

67     static void testScript(Connection JavaDoc aConnection, String JavaDoc aPath) {
68
69         try {
70             Statement JavaDoc statement = aConnection.createStatement();
71             File JavaDoc testfile = new File JavaDoc(aPath);
72             LineNumberReader JavaDoc reader =
73                 new LineNumberReader JavaDoc(new FileReader JavaDoc(testfile));
74             HsqlArrayList section = null;
75
76             print("Opened test script file: " + testfile.getAbsolutePath());
77
78             /**
79              * we read the lines from the start of one section of the script "/*"
80              * until the start of the next section, collecting the lines
81              * in the Vector lines.
82              * When a new section starts, we will pass the vector of lines
83              * to the test method to be processed.
84              */

85             int startLine = 1;
86
87             while (true) {
88                 boolean startSection = false;
89                 String JavaDoc line = reader.readLine();
90
91                 if (line == null) {
92                     break;
93                 }
94
95                 line = line.substring(
96                     0, org.hsqldb.lib.StringUtil.rTrimSize(line));
97
98                 //if the line is blank or a comment, then ignore it
99
if ((line.length() == 0) || line.startsWith("--")) {
100                     continue;
101                 }
102
103                 //...check if we're starting a new section...
104
if (line.startsWith("/*")) {
105                     startSection = true;
106                 }
107
108                 if (line.charAt(0) != ' ' && line.charAt(0) != '*') {
109                     startSection = true;
110                 }
111
112                 if (startSection) {
113
114                     //...if we are, test the previous section (if it exists)...
115
if (section != null) {
116                         testSection(statement, section, startLine);
117                     }
118
119                     //...and then start a new section...
120
section = new HsqlArrayList();
121                     startLine = reader.getLineNumber();
122                 }
123
124                 section.add(line);
125             }
126
127             //send the last section for testing
128
if (section != null) {
129                 testSection(statement, section, startLine);
130             }
131
132             statement.close();
133             print("Processed lines: " + reader.getLineNumber());
134         } catch (Exception JavaDoc e) {
135             e.printStackTrace();
136             print("test script file error: " + e.getMessage());
137         }
138     }
139
140     /**
141      * Performs a preformatted statement or group of statements and throws
142      * if the result does not match the expected one.
143      * @param line start line in the script file for this test
144      * @param stat Statement object used to access the database
145      * @param s Contains the type, expected result and SQL for the test
146      */

147     static void test(Statement JavaDoc stat, String JavaDoc s, int line) {
148
149         //maintain the interface for this method
150
HsqlArrayList section = new HsqlArrayList();
151
152         section.add(s);
153         testSection(stat, section, line);
154     }
155
156     /**
157      * Method to save typing ;-)
158      * @param s String to be printed
159      */

160     static void print(String JavaDoc s) {
161         System.out.println(s);
162     }
163
164     /**
165      * Takes a discrete section of the test script, contained in the
166      * section vector, splits this into the expected result(s) and
167      * submits the statement to the database, comparing the results
168      * returned with the expected results.
169      * If the actual result differs from that expected, or an
170      * exception is thrown, then the appropriate message is printed.
171      * @param stat Statement object used to access the database
172      * @param section Vector of script lines containing a discrete
173      * section of script (i.e. test type, expected results,
174      * SQL for the statement).
175      * @param line line of the script file where this section started
176      */

177     private static void testSection(Statement JavaDoc stat, HsqlArrayList section,
178                                     int line) {
179
180         //create an appropriate instance of ParsedSection
181
ParsedSection pSection = parsedSectionFactory(section);
182
183         if (pSection == null) { //it was not possible to sucessfully parse the section
184
print("The section starting at line " + line
185                   + " could not be parsed, " + "and so was not processed.\n");
186         } else if (pSection instanceof IgnoreParsedSection) {
187             print("Line " + line + ": " + pSection.getResultString());
188         } else if (pSection instanceof DisplaySection) {
189
190             // May or may not want to report line number for 'd' sections. ?
191
print(pSection.getResultString());
192         } else if (!pSection.test(stat)) {
193             print("section starting at line " + line);
194             print("returned an unexpected result:");
195             print(pSection.toString());
196         }
197     }
198
199     /**
200      * Factory method to create appropriate parsed section class for the section
201      * @param aSection Vector containing the section of script
202      * @return a ParesedSection object
203      */

204     private static ParsedSection parsedSectionFactory(
205             HsqlArrayList aSection) {
206
207         //type of the section
208
char type = ' ';
209
210         //section represented as an array of Strings, one for each significant
211
//line in the section
212
String JavaDoc[] rows = null;
213
214         //read the first line of the Vector...
215
String JavaDoc topLine = (String JavaDoc) aSection.get(0);
216
217         //...and check it for the type...
218
if (topLine.startsWith("/*")) {
219             type = topLine.charAt(2);
220
221             //if the type code is invalid return null
222
if (!ParsedSection.isValidCode(type)) {
223                 return null;
224             }
225
226             //if the type code is UPPERCASE and system property IgnoreCodeCase
227
//has been set to true, make the type code lowercase
228
if ((Character.isUpperCase(type))
229                     && (Boolean.getBoolean("IgnoreCodeCase"))) {
230                 type = Character.toLowerCase(type);
231             }
232
233             //...strip out the type declaration...
234
topLine = topLine.substring(3);
235         }
236
237         //if, after stripping out the declaration from topLine, the length of topLine
238
//is greater than 0, then keep the rest of the line, as the first row.
239
//Otherwise it will be discarded, and the offset (between the array and the vector)
240
//set to 1.
241
int offset = 0;
242
243         if (topLine.trim().length() > 0) {
244             rows = new String JavaDoc[aSection.size()];
245             rows[0] = topLine;
246         } else {
247             rows = new String JavaDoc[aSection.size() - 1];
248             offset = 1;
249         }
250
251         //pull the rest of aSection into the rows array.
252
for (int i = (1 - offset); i < rows.length; i++) {
253             rows[i] = (String JavaDoc) aSection.get(i + offset);
254         }
255
256         //then pass this to the constructor for the ParsedSection class that
257
//corresponds to the value of type
258
switch (type) {
259
260             case 'u' :
261                 return new UpdateParsedSection(rows);
262
263             case 's' :
264                 return new SilentParsedSection(rows);
265
266             case 'r' :
267                 return new ResultSetParsedSection(rows);
268
269             case 'c' :
270                 return new CountParsedSection(rows);
271
272             case 'd' :
273                 return new DisplaySection(rows);
274
275             case 'e' :
276                 return new ExceptionParsedSection(rows);
277
278             case ' ' :
279                 return new BlankParsedSection(rows);
280
281             default :
282
283                 //if we arrive here, then we should have a valid code,
284
//since we validated it earlier, so return an
285
//IgnoreParsedSection object
286
return new IgnoreParsedSection(rows, type);
287         }
288     }
289 }
290
291 /**
292  * Abstract inner class representing a parsed section of script.
293  * The specific ParsedSections for each type of test should inherit from this.
294  */

295 abstract class ParsedSection {
296
297     /**
298      * Type of this test.
299      * @see isValidCase() for allowed values
300      */

301     protected char type = ' ';
302
303     /** error message for this section */
304     String JavaDoc message = null;
305
306     /** contents of the section as an array of Strings, one for each line in the section. */
307     protected String JavaDoc[] lines = null;
308
309     /** number of the last row containing results in sectionLines */
310     protected int resEndRow = 0;
311
312     /** SQL query to be submitted to the database. */
313     protected String JavaDoc sqlString = null;
314
315     /**
316      * Constructor when the section's input lines do not need to be parsed
317      * into SQL.
318      */

319     protected ParsedSection() {}
320
321     /**
322      * Common constructor functions for this family.
323      * @param aLines Array of the script lines containing the section of script.
324      * database
325      */

326     protected ParsedSection(String JavaDoc[] aLines) {
327
328         lines = aLines;
329
330         //read the lines array backwards to get out the SQL String
331
//using a StringBuffer for efficency until we've got the whole String
332
StringBuffer JavaDoc sqlBuff = new StringBuffer JavaDoc();
333         int endIndex = 0;
334         int k = lines.length - 1;
335
336         do {
337
338             //check to see if the row contains the end of the result set
339
if ((endIndex = lines[k].indexOf("*/")) != -1) {
340
341                 //then this is the end of the result set
342
sqlBuff.insert(0, lines[k].substring(endIndex + 2));
343
344                 lines[k] = lines[k].substring(0, endIndex);
345
346                 if (lines[k].length() == 0) {
347                     resEndRow = k - 1;
348                 } else {
349                     resEndRow = k;
350                 }
351
352                 break;
353             } else {
354                 sqlBuff.insert(0, lines[k]);
355             }
356
357             k--;
358         } while (k >= 0);
359
360         //set sqlString value
361
sqlString = sqlBuff.toString();
362     }
363
364     /**
365      * String representation of this ParsedSection
366      * @return String representation of this ParsedSection
367      */

368     public String JavaDoc toString() {
369
370         StringBuffer JavaDoc b = new StringBuffer JavaDoc();
371
372         b.append("\n******\n");
373         b.append("contents of lines array:\n");
374
375         for (int i = 0; i < lines.length; i++) {
376             if (lines[i].trim().length() > 0) {
377                 b.append("line ").append(i).append(": ").append(
378                     lines[i]).append("\n");
379             }
380         }
381
382         b.append("Type: ");
383         b.append(getType()).append("\n");
384         b.append("SQL: ").append(getSql()).append("\n");
385         b.append("results:\n");
386         b.append(getResultString());
387
388         //check to see if the message field has been populated
389
if (getMessage() != null) {
390             b.append("\nmessage:\n");
391             b.append(getMessage());
392         }
393
394         b.append("\n******\n");
395
396         return b.toString();
397     }
398
399     /**
400      * returns a String representation of the expected result for the test
401      * @return The expected result(s) for the test
402      */

403     protected abstract String JavaDoc getResultString();
404
405     /**
406      * returns the error message for the section
407      *
408      * @return message
409      */

410     protected String JavaDoc getMessage() {
411         return message;
412     }
413
414     /**
415      * returns the type of this section
416      * @return type of this section
417      */

418     protected char getType() {
419         return type;
420     }
421
422     /**
423      * returns the SQL statement for this section
424      * @return SQL statement for this section
425      */

426     protected String JavaDoc getSql() {
427         return sqlString;
428     }
429
430     /**
431      * performs the test contained in the section against the database.
432      * @param aStatement Statement object
433      * @return true if the result(s) are as expected, otherwise false
434      */

435     protected boolean test(Statement JavaDoc aStatement) {
436
437         try {
438             aStatement.execute(getSql());
439         } catch (Exception JavaDoc x) {
440             message = x.getMessage();
441
442             return false;
443         }
444
445         return true;
446     }
447
448     /**
449      * Checks that the type code letter is valid
450      * @param aCode type code to validate.
451      * @return true if the type code is valid, otherwise false.
452      */

453     protected static boolean isValidCode(char aCode) {
454
455         /* Allowed values for test codes are:
456          * (note that UPPERCASE codes, while valid are only processed if the
457          * system property IgnoreCodeCase has been set to true)
458          *
459          * 'u' ('U') - update
460          * 'c' ('C') - count
461          * 'e' ('E') - exception
462          * 'r' ('R') - results
463          * 's' ('S') - silent
464          * 'd' - display (No reason to use upper-case).
465          * ' ' - not a test
466          */

467         char testChar = Character.toLowerCase(aCode);
468
469         switch (testChar) {
470
471             case ' ' :
472             case 'r' :
473             case 'e' :
474             case 'c' :
475             case 'u' :
476             case 's' :
477             case 'd' :
478                 return true;
479         }
480
481         return false;
482     }
483 }
484
485 /** Represents a ParsedSection for a ResultSet test */
486 class ResultSetParsedSection extends ParsedSection {
487
488     private String JavaDoc delim = System.getProperty("TestUtilFieldDelimiter", ",");
489     private String JavaDoc[] expectedRows = null;
490
491     /**
492      * constructs a new instance of ResultSetParsedSection, interpreting
493      * the supplied results as one or more lines of delimited field values
494      * @param lines String[]
495      */

496     protected ResultSetParsedSection(String JavaDoc[] lines) {
497
498         super(lines);
499
500         type = 'r';
501
502         //now we'll populate the expectedResults array
503
expectedRows = new String JavaDoc[(resEndRow + 1)];
504
505         for (int i = 0; i <= resEndRow; i++) {
506             int skip = StringUtil.skipSpaces(lines[i], 0);
507
508             expectedRows[i] = lines[i].substring(skip);
509         }
510     }
511
512     protected String JavaDoc getResultString() {
513
514         StringBuffer JavaDoc printVal = new StringBuffer JavaDoc();
515
516         for (int i = 0; i < getExpectedRows().length; i++) {
517             printVal.append(getExpectedRows()[i]).append("\n");
518         }
519
520         return printVal.toString();
521     }
522
523     protected boolean test(Statement JavaDoc aStatement) {
524
525         try {
526             try {
527
528                 //execute the SQL
529
aStatement.execute(getSql());
530             } catch (SQLException JavaDoc s) {
531                 throw new Exception JavaDoc(
532                     "Expected a ResultSet, but got the error: "
533                     + s.getMessage());
534             }
535
536             //check that update count != -1
537
if (aStatement.getUpdateCount() != -1) {
538                 throw new Exception JavaDoc(
539                     "Expected a ResultSet, but got an update count of "
540                     + aStatement.getUpdateCount());
541             }
542
543             //iterate over the ResultSet
544
ResultSet JavaDoc results = aStatement.getResultSet();
545             int count = 0;
546
547             while (results.next()) {
548                 if (count < getExpectedRows().length) {
549
550 // String[] expectedFields = getExpectedRows()[count].split(delim);
551
String JavaDoc[] expectedFields =
552                         StringUtil.split(getExpectedRows()[count], delim);
553
554                     //check that we have the number of columns expected...
555
if (results.getMetaData().getColumnCount()
556                             == expectedFields.length) {
557
558                         //...and if so, check that the column values are as expected...
559
int j = 0;
560
561                         for (int i = 0; i < expectedFields.length; i++) {
562                             j = i + 1;
563
564                             String JavaDoc actual = results.getString(j);
565
566                             //...including null values...
567
if (actual == null) { //..then we have a null
568

569                                 //...check to see if we were expecting it...
570
if (!expectedFields[i].equalsIgnoreCase(
571                                         "NULL")) {
572                                     throw new Exception JavaDoc(
573                                         "Expected row " + count
574                                         + " of the ResultSet to contain:\n"
575                                         + getExpectedRows()[count]
576                                         + "\nbut field " + j
577                                         + " contained NULL");
578                                 }
579                             } else if (!actual.equals(expectedFields[i])) {
580
581                                 //then the results are different
582
throw new Exception JavaDoc(
583                                     "Expected row " + (count + 1)
584                                     + " of the ResultSet to contain:\n"
585                                     + getExpectedRows()[count]
586                                     + "\nbut field " + j + " contained "
587                                     + results.getString(j));
588                             }
589                         }
590                     } else {
591
592                         //we have the wrong number of columns
593
throw new Exception JavaDoc(
594                             "Expected the ResultSet to contain "
595                             + expectedFields.length
596                             + " fields, but it contained "
597                             + results.getMetaData().getColumnCount()
598                             + " fields.");
599                     }
600                 }
601
602                 count++;
603             }
604
605             //check that we got as many rows as expected
606
if (count != getExpectedRows().length) {
607
608                 //we don't have the expected number of rows
609
throw new Exception JavaDoc("Expected the ResultSet to contain "
610                                     + getExpectedRows().length
611                                     + " rows, but it contained " + count
612                                     + " rows.");
613             }
614         } catch (Exception JavaDoc x) {
615             message = x.getMessage();
616
617             return false;
618         }
619
620         return true;
621     }
622
623     private String JavaDoc[] getExpectedRows() {
624         return expectedRows;
625     }
626 }
627
628 /** Represents a ParsedSection for an update test */
629 class UpdateParsedSection extends ParsedSection {
630
631     //expected update count
632
int countWeWant;
633
634     protected UpdateParsedSection(String JavaDoc[] lines) {
635
636         super(lines);
637
638         type = 'u';
639         countWeWant = Integer.parseInt(lines[0]);
640     }
641
642     protected String JavaDoc getResultString() {
643         return Integer.toString(getCountWeWant());
644     }
645
646     private int getCountWeWant() {
647         return countWeWant;
648     }
649
650     protected boolean test(Statement JavaDoc aStatement) {
651
652         try {
653             try {
654
655                 //execute the SQL
656
aStatement.execute(getSql());
657             } catch (SQLException JavaDoc s) {
658                 throw new Exception JavaDoc("Expected an update count of "
659                                     + getCountWeWant()
660                                     + ", but got the error: "
661                                     + s.getMessage());
662             }
663
664             if (aStatement.getUpdateCount() != getCountWeWant()) {
665                 throw new Exception JavaDoc("Expected an update count of "
666                                     + getCountWeWant()
667                                     + ", but got an update count of "
668                                     + aStatement.getUpdateCount() + ".");
669             }
670         } catch (Exception JavaDoc x) {
671             message = x.getMessage();
672
673             return false;
674         }
675
676         return true;
677     }
678 }
679
680 /** Represents a ParsedSection for silent execution */
681 class SilentParsedSection extends ParsedSection {
682
683     protected SilentParsedSection(String JavaDoc[] lines) {
684
685         super(lines);
686
687         type = 's';
688     }
689
690     protected String JavaDoc getResultString() {
691         return null;
692     }
693
694     protected boolean test(Statement JavaDoc aStatement) {
695
696         try {
697             aStatement.execute(getSql());
698         } catch (Exception JavaDoc x) {}
699
700         return true;
701     }
702 }
703
704 /** Represents a ParsedSection for a count test */
705 class CountParsedSection extends ParsedSection {
706
707     //expected row count
708
private int countWeWant;
709
710     protected CountParsedSection(String JavaDoc[] lines) {
711
712         super(lines);
713
714         type = 'c';
715         countWeWant = Integer.parseInt(lines[0]);
716     }
717
718     protected String JavaDoc getResultString() {
719         return Integer.toString(getCountWeWant());
720     }
721
722     private int getCountWeWant() {
723         return countWeWant;
724     }
725
726     protected boolean test(Statement JavaDoc aStatement) {
727
728         try {
729
730             //execute the SQL
731
try {
732                 aStatement.execute(getSql());
733             } catch (SQLException JavaDoc s) {
734                 throw new Exception JavaDoc("Expected a ResultSet containing "
735                                     + getCountWeWant()
736                                     + " rows, but got the error: "
737                                     + s.getMessage());
738             }
739
740             //check that update count != -1
741
if (aStatement.getUpdateCount() != -1) {
742                 throw new Exception JavaDoc(
743                     "Expected a ResultSet, but got an update count of "
744                     + aStatement.getUpdateCount());
745             }
746
747             //iterate over the ResultSet
748
ResultSet JavaDoc results = aStatement.getResultSet();
749             int count = 0;
750
751             while (results.next()) {
752                 count++;
753             }
754
755             //check that we got as many rows as expected
756
if (count != getCountWeWant()) {
757
758                 //we don't have the expected number of rows
759
throw new Exception JavaDoc("Expected the ResultSet to contain "
760                                     + getCountWeWant()
761                                     + " rows, but it contained " + count
762                                     + " rows.");
763             }
764         } catch (Exception JavaDoc x) {
765             message = x.getMessage();
766
767             return false;
768         }
769
770         return true;
771     }
772 }
773
774 /** Represents a ParsedSection for an Exception test */
775 class ExceptionParsedSection extends ParsedSection {
776
777     protected ExceptionParsedSection(String JavaDoc[] lines) {
778
779         super(lines);
780
781         type = 'e';
782     }
783
784     protected String JavaDoc getResultString() {
785         return "SQLException";
786     }
787
788     protected boolean test(Statement JavaDoc aStatement) {
789
790         try {
791             aStatement.execute(getSql());
792         } catch (SQLException JavaDoc sqlX) {
793             return true;
794         } catch (Exception JavaDoc x) {
795             message = x.getMessage();
796
797             return false;
798         }
799
800         return false;
801     }
802 }
803
804 /** Represents a ParsedSection for a section with blank type */
805 class BlankParsedSection extends ParsedSection {
806
807     protected BlankParsedSection(String JavaDoc[] lines) {
808
809         super(lines);
810
811         type = ' ';
812     }
813
814     protected String JavaDoc getResultString() {
815         return "No result specified for this section";
816     }
817 }
818
819 /** Represents a ParsedSection that is to be ignored */
820 class IgnoreParsedSection extends ParsedSection {
821
822     protected IgnoreParsedSection(String JavaDoc[] inLines, char aType) {
823
824         /* Extremely ambiguous to use input parameter of same exact
825          * variable name as the superclass member "lines".
826          * Therefore, renaming to inLines. */

827
828         // Inefficient to parse this into SQL when we aren't going to use
829
// it as SQL. Should probably just be removed to use the
830
// super() constructor.
831
super(inLines);
832
833         type = aType;
834     }
835
836     protected String JavaDoc getResultString() {
837         return "This section, of type '" + getType() + "' was ignored";
838     }
839 }
840
841 /** Represents a Section to be Displayed, not executed */
842 class DisplaySection extends ParsedSection {
843
844     protected DisplaySection(String JavaDoc[] inLines) {
845
846         /* Can't user the super constructor, since it does funny things when
847          * constructing the SQL Buffer, which we don't need. */

848         lines = inLines;
849
850         int firstSlash = lines[0].indexOf('/');
851
852         lines[0] = lines[0].substring(firstSlash + 1);
853     }
854
855     protected String JavaDoc getResultString() {
856
857         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
858
859         for (int i = 0; i < lines.length; i++) {
860             if (i > 0) {
861                 sb.append('\n');
862             }
863
864             sb.append("+ " + lines[i]);
865         }
866
867         return sb.toString();
868     }
869 }
870
Popular Tags