JavadocTester.java revision 173:fdfed22db054
198944Sobrien/* 298944Sobrien * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. 398944Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 498944Sobrien * 598944Sobrien * This code is free software; you can redistribute it and/or modify it 698944Sobrien * under the terms of the GNU General Public License version 2 only, as 798944Sobrien * published by the Free Software Foundation. 898944Sobrien * 998944Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT 1098944Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1198944Sobrien * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1298944Sobrien * version 2 for more details (a copy is included in the LICENSE file that 1398944Sobrien * accompanied this code). 1498944Sobrien * 1598944Sobrien * You should have received a copy of the GNU General Public License version 1698944Sobrien * 2 along with this work; if not, write to the Free Software Foundation, 1798944Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1898944Sobrien * 1998944Sobrien * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 2098944Sobrien * CA 95054 USA or visit www.sun.com if you need additional information or 2198944Sobrien * have any questions. 2298944Sobrien */ 2398944Sobrien 2498944Sobrienimport com.sun.javadoc.*; 2598944Sobrienimport java.util.*; 2698944Sobrienimport java.io.*; 2798944Sobrien 2898944Sobrien 2998944Sobrien/** 30130803Smarcel * Runs javadoc and then runs regression tests on the resulting output. 3198944Sobrien * This class currently contains three tests: 3298944Sobrien * <ul> 3398944Sobrien * <li> String search: Reads each file, complete with newlines, 3498944Sobrien * into a string. Lets you search for strings that contain 3598944Sobrien * newlines. String matching is case-sensitive. 3698944Sobrien * You can run javadoc multiple times with different arguments, 3798944Sobrien * generating output into different destination directories, and 3898944Sobrien * then perform a different array of tests on each one. 3998944Sobrien * To do this, the run method accepts a test array for testing 4098944Sobrien * that a string is found, and a negated test array for testing 4198944Sobrien * that a string is not found. 4298944Sobrien * <li> Run diffs: Iterate through the list of given file pairs 4398944Sobrien * and diff the pairs. 4498944Sobrien * <li> Check exit code: Check the exit code of Javadoc and 4598944Sobrien * record whether the test passed or failed. 4698944Sobrien * </ul> 4798944Sobrien * 4898944Sobrien * @author Doug Kramer 4998944Sobrien * @author Jamie Ho 5098944Sobrien * @since 1.4.2 5198944Sobrien */ 5298944Sobrienpublic abstract class JavadocTester { 5398944Sobrien 5498944Sobrien protected static final String FS = System.getProperty("file.separator"); 5598944Sobrien protected static final String PS = System.getProperty("path.separator"); 5698944Sobrien protected static final String NL = System.getProperty("line.separator"); 5798944Sobrien protected static final String SRC_DIR = System.getProperty("test.src", "."); 5898944Sobrien protected static final String JAVA_VERSION = System.getProperty("java.version"); 5998944Sobrien protected static final String[][] NO_TEST = new String[][] {}; 6098944Sobrien 6198944Sobrien /** 6298944Sobrien * Use this as the file name in the test array when you want to search 6398944Sobrien * for a string in the error output. 6498944Sobrien */ 6598944Sobrien public static final String ERROR_OUTPUT = "ERROR_OUTPUT"; 6698944Sobrien 6798944Sobrien /** 6898944Sobrien * Use this as the file name in the test array when you want to search 6998944Sobrien * for a string in the notice output. 7098944Sobrien */ 7198944Sobrien public static final String NOTICE_OUTPUT = "NOTICE_OUTPUT"; 7298944Sobrien 7398944Sobrien /** 7498944Sobrien * Use this as the file name in the test array when you want to search 7598944Sobrien * for a string in the warning output. 7698944Sobrien */ 7798944Sobrien public static final String WARNING_OUTPUT = "WARNING_OUTPUT"; 7898944Sobrien 7998944Sobrien /** 8098944Sobrien * Use this as the file name in the test array when you want to search 8198944Sobrien * for a string in standard output. 8298944Sobrien */ 8398944Sobrien public static final String STANDARD_OUTPUT = "STANDARD_OUTPUT"; 8498944Sobrien 8598944Sobrien /** 8698944Sobrien * The default doclet. 8798944Sobrien */ 8898944Sobrien public static final String DEFAULT_DOCLET_CLASS = "com.sun.tools.doclets.formats.html.HtmlDoclet"; 8998944Sobrien public static final String DEFAULT_DOCLET_CLASS_OLD = "com.sun.tools.doclets.standard.Standard"; 9098944Sobrien 9198944Sobrien /** 9298944Sobrien * The writer to write error messages. 9398944Sobrien */ 9498944Sobrien public StringWriter errors; 9598944Sobrien 9698944Sobrien /** 9798944Sobrien * The writer to write notices. 9898944Sobrien */ 9998944Sobrien public StringWriter notices; 10098944Sobrien 10198944Sobrien /** 10298944Sobrien * The writer to write warnings. 10398944Sobrien */ 10498944Sobrien public StringWriter warnings; 10598944Sobrien 10698944Sobrien /** 10798944Sobrien * The buffer of warning output.. 10898944Sobrien */ 10998944Sobrien public StringBuffer standardOut; 11098944Sobrien 11198944Sobrien /** 11298944Sobrien * The current subtest number. 11398944Sobrien */ 11498944Sobrien private static int numTestsRun = 0; 11598944Sobrien 11698944Sobrien /** 11798944Sobrien * The number of subtests passed. 11898944Sobrien */ 11998944Sobrien private static int numTestsPassed = 0; 12098944Sobrien 12198944Sobrien /** 12298944Sobrien * The current run of javadoc 12398944Sobrien */ 12498944Sobrien private static int javadocRunNum = 0; 12598944Sobrien 12698944Sobrien /** 12798944Sobrien * Construct a JavadocTester. 12898944Sobrien */ 12998944Sobrien public JavadocTester() { 13098944Sobrien } 13198944Sobrien 13298944Sobrien /** 13398944Sobrien * Return the bug id. 13498944Sobrien * @return the bug id 13598944Sobrien */ 13698944Sobrien public abstract String getBugId(); 13798944Sobrien 13898944Sobrien /** 13998944Sobrien * Return the name of the bug. 14098944Sobrien * @return the name of the bug 14198944Sobrien */ 14298944Sobrien public abstract String getBugName(); 14398944Sobrien 14498944Sobrien /** 14598944Sobrien * Execute the tests. 14698944Sobrien * 14798944Sobrien * @param tester the tester to execute 14898944Sobrien * @param args the arguments to pass to Javadoc 14998944Sobrien * @param testArray the array of tests 15098944Sobrien * @param negatedTestArray the array of negated tests 15198944Sobrien * @return the return code for the execution of Javadoc 15298944Sobrien */ 15398944Sobrien public static int run(JavadocTester tester, String[] args, 15498944Sobrien String[][] testArray, String[][] negatedTestArray) { 15598944Sobrien int returnCode = tester.runJavadoc(args); 15698944Sobrien tester.runTestsOnHTML(testArray, negatedTestArray); 15798944Sobrien return returnCode; 15898944Sobrien } 15998944Sobrien 16098944Sobrien /** 16198944Sobrien * Execute Javadoc using the default doclet. 16298944Sobrien * 16398944Sobrien * @param args the arguments to pass to Javadoc 16498944Sobrien * @return the return code from the execution of Javadoc 16598944Sobrien */ 16698944Sobrien public int runJavadoc(String[] args) { 16798944Sobrien float javaVersion = Float.parseFloat(JAVA_VERSION.substring(0,3)); 16898944Sobrien String docletClass = javaVersion < 1.5 ? 16998944Sobrien DEFAULT_DOCLET_CLASS_OLD : DEFAULT_DOCLET_CLASS; 17098944Sobrien return runJavadoc(docletClass, args); 17198944Sobrien } 17298944Sobrien 17398944Sobrien 17498944Sobrien /** 17598944Sobrien * Execute Javadoc. 17698944Sobrien * 17798944Sobrien * @param docletClass the doclet being tested. 17898944Sobrien * @param args the arguments to pass to Javadoc 17998944Sobrien * @return the return code from the execution of Javadoc 18098944Sobrien */ 18198944Sobrien public int runJavadoc(String docletClass, String[] args) { 18298944Sobrien javadocRunNum++; 18398944Sobrien if (javadocRunNum == 1) { 18498944Sobrien System.out.println("\n" + "Running javadoc..."); 18598944Sobrien } else { 18698944Sobrien System.out.println("\n" + "Running javadoc (run " 18798944Sobrien + javadocRunNum + ")..."); 18898944Sobrien } 18998944Sobrien initOutputBuffers(); 19098944Sobrien 19198944Sobrien ByteArrayOutputStream stdout = new ByteArrayOutputStream(); 19298944Sobrien PrintStream prev = System.out; 19398944Sobrien System.setOut(new PrintStream(stdout)); 19498944Sobrien int returnCode = com.sun.tools.javadoc.Main.execute( 19598944Sobrien getBugName(), 19698944Sobrien new PrintWriter(errors, true), 19798944Sobrien new PrintWriter(warnings, true), 19898944Sobrien new PrintWriter(notices, true), 19998944Sobrien docletClass, 20098944Sobrien getClass().getClassLoader(), 20198944Sobrien args); 20298944Sobrien System.setOut(prev); 20398944Sobrien standardOut = new StringBuffer(stdout.toString()); 20498944Sobrien printJavadocOutput(); 20598944Sobrien return returnCode; 20698944Sobrien } 20798944Sobrien 208130803Smarcel /** 209130803Smarcel * Create new string writer buffers 210130803Smarcel */ 211130803Smarcel private void initOutputBuffers() { 212130803Smarcel errors = new StringWriter(); 213130803Smarcel notices = new StringWriter(); 214130803Smarcel warnings = new StringWriter(); 215130803Smarcel } 216130803Smarcel 217130803Smarcel /** 218130803Smarcel * Run array of tests on the resulting HTML. 219130803Smarcel * This method accepts a testArray for testing that a string is found 220130803Smarcel * and a negatedTestArray for testing that a string is not found. 221130803Smarcel * 222130803Smarcel * @param testArray the array of tests 223130803Smarcel * @param negatedTestArray the array of negated tests 224130803Smarcel */ 225130803Smarcel public void runTestsOnHTML(String[][] testArray, String[][] negatedTestArray) { 226130803Smarcel runTestsOnHTML(testArray, false); 227130803Smarcel runTestsOnHTML(negatedTestArray, true); 228130803Smarcel } 229130803Smarcel 230130803Smarcel /** 231130803Smarcel * Run the array of tests on the resulting HTML. 232130803Smarcel * 233130803Smarcel * @param testArray the array of tests 234130803Smarcel * @param isNegated true if test is negated; false otherwise 235130803Smarcel */ 236130803Smarcel private void runTestsOnHTML(String[][] testArray , boolean isNegated) { 237130803Smarcel for (int i = 0; i < testArray.length; i++) { 238130803Smarcel 239130803Smarcel numTestsRun++; 240130803Smarcel 24198944Sobrien System.out.print("Running subtest #" + numTestsRun + "... "); 242130803Smarcel 243130803Smarcel // Get string to find 244130803Smarcel String stringToFind = testArray[i][1]; 245130803Smarcel 246130803Smarcel // Read contents of file into a string 247130803Smarcel String fileString; 248130803Smarcel try { 249130803Smarcel fileString = readFileToString(testArray[i][0]); 25098944Sobrien } catch (Error e) { 251130803Smarcel if (isNegated) { 25298944Sobrien numTestsPassed += 1; 25398944Sobrien System.out.println("Passed\n not found:\n" 25498944Sobrien + stringToFind + " in non-existent " + testArray[i][0] + "\n"); 255130803Smarcel continue; 25698944Sobrien } 25798944Sobrien throw e; 258130803Smarcel } 259130803Smarcel // Find string in file's contents 26098944Sobrien boolean isFound = findString(fileString, stringToFind); 26198944Sobrien if ((isNegated && !isFound) || (!isNegated && isFound) ) { 26298944Sobrien numTestsPassed += 1; 26398944Sobrien System.out.println( "Passed" + "\n" 26498944Sobrien + (isNegated ? "not found:" : "found:") + "\n" 26598944Sobrien + stringToFind + " in " + testArray[i][0] + "\n"); 26698944Sobrien } else { 26798944Sobrien System.out.println( "FAILED" + "\n" 268130803Smarcel + "for bug " + getBugId() 269130803Smarcel + " (" + getBugName() + ")" + "\n" 270130803Smarcel + "when searching for:" + "\n" 271130803Smarcel + stringToFind 272130803Smarcel + " in " + testArray[i][0] + "\n"); 27398944Sobrien } 27498944Sobrien } 27598944Sobrien } 27698944Sobrien 27798944Sobrien /** 27898944Sobrien * Iterate through the list of given file pairs and diff each file. 27998944Sobrien * 28098944Sobrien * @param filePairs the pairs of files to diff. 28198944Sobrien * @throws an Error is thrown if any differences are found between 28298944Sobrien * file pairs. 28398944Sobrien */ 28498944Sobrien public void runDiffs(String[][] filePairs) throws Error { 28598944Sobrien runDiffs(filePairs, true); 28698944Sobrien } 28798944Sobrien 288130803Smarcel /** 289130803Smarcel * Iterate through the list of given file pairs and diff each file. 29098944Sobrien * 29198944Sobrien * @param filePairs the pairs of files to diff. 29298944Sobrien * @param throwErrorIFNoMatch flag to indicate whether or not to throw 29398944Sobrien * an error if the files do not match. 29498944Sobrien * 29598944Sobrien * @throws an Error is thrown if any differences are found between 29698944Sobrien * file pairs and throwErrorIFNoMatch is true. 297130803Smarcel */ 29898944Sobrien public void runDiffs(String[][] filePairs, boolean throwErrorIfNoMatch) throws Error { 29998944Sobrien for (int i = 0; i < filePairs.length; i++) { 30098944Sobrien diff(filePairs[i][0], filePairs[i][1], throwErrorIfNoMatch); 30198944Sobrien } 30298944Sobrien } 30398944Sobrien 304130803Smarcel /** 305130803Smarcel * Check the exit code of Javadoc and record whether the test passed 306130803Smarcel * or failed. 307130803Smarcel * 30898944Sobrien * @param expectedExitCode The exit code that is required for the test 30998944Sobrien * to pass. 31098944Sobrien * @param actualExitCode The actual exit code from the previous run of 31198944Sobrien * Javadoc. 31298944Sobrien */ 31398944Sobrien public void checkExitCode(int expectedExitCode, int actualExitCode) { 314130803Smarcel numTestsRun++; 315130803Smarcel if (expectedExitCode == actualExitCode) { 31698944Sobrien System.out.println( "Passed" + "\n" + " got return code " + 31798944Sobrien actualExitCode); 31898944Sobrien numTestsPassed++; 31998944Sobrien } else { 32098944Sobrien System.out.println( "FAILED" + "\n" + "for bug " + getBugId() 32198944Sobrien + " (" + getBugName() + ")" + "\n" + "Expected return code " + 32298944Sobrien expectedExitCode + " but got " + actualExitCode); 32398944Sobrien } 32498944Sobrien } 32598944Sobrien 32698944Sobrien /** 32798944Sobrien * Print a summary of the test results. 32898944Sobrien */ 32998944Sobrien protected void printSummary() { 33098944Sobrien if ( numTestsRun != 0 && numTestsPassed == numTestsRun ) { 33198944Sobrien // Test passed 332130803Smarcel System.out.println("\n" + "All " + numTestsPassed 333130803Smarcel + " subtests passed"); 33498944Sobrien } else { 33598944Sobrien // Test failed 33698944Sobrien throw new Error("\n" + (numTestsRun - numTestsPassed) 33798944Sobrien + " of " + (numTestsRun) 33898944Sobrien + " subtests failed for bug " + getBugId() 33998944Sobrien + " (" + getBugName() + ")" + "\n"); 34098944Sobrien } 34198944Sobrien } 34298944Sobrien 34398944Sobrien /** 34498944Sobrien * Print the output stored in the buffers. 34598944Sobrien */ 34698944Sobrien protected void printJavadocOutput() { 34798944Sobrien System.out.println(STANDARD_OUTPUT + " : \n" + getStandardOutput()); 34898944Sobrien System.err.println(ERROR_OUTPUT + " : \n" + getErrorOutput()); 34998944Sobrien System.err.println(WARNING_OUTPUT + " : \n" + getWarningOutput()); 35098944Sobrien System.out.println(NOTICE_OUTPUT + " : \n" + getNoticeOutput()); 35198944Sobrien } 35298944Sobrien 35398944Sobrien /** 35498944Sobrien * Read the file and return it as a string. 35598944Sobrien * 35698944Sobrien * @param fileName the name of the file to read 35798944Sobrien * @return the file in string format 35898944Sobrien */ 35998944Sobrien public String readFileToString(String fileName) throws Error { 36098944Sobrien if (fileName.equals(ERROR_OUTPUT)) { 36198944Sobrien return getErrorOutput(); 36298944Sobrien } else if (fileName.equals(NOTICE_OUTPUT)) { 36398944Sobrien return getNoticeOutput(); 36498944Sobrien } else if (fileName.equals(WARNING_OUTPUT)) { 36598944Sobrien return getWarningOutput(); 36698944Sobrien } else if (fileName.equals(STANDARD_OUTPUT)) { 36798944Sobrien return getStandardOutput(); 36898944Sobrien } 36998944Sobrien try { 37098944Sobrien File file = new File(fileName); 37198944Sobrien if ( !file.exists() ) { 37298944Sobrien System.out.println("\n" + "FILE DOES NOT EXIST: " + fileName); 37398944Sobrien } 37498944Sobrien BufferedReader in = new BufferedReader(new FileReader(file)); 37598944Sobrien 37698944Sobrien // Create an array of characters the size of the file 37798944Sobrien char[] allChars = new char[(int)file.length()]; 37898944Sobrien 37998944Sobrien // Read the characters into the allChars array 38098944Sobrien in.read(allChars, 0, (int)file.length()); 38198944Sobrien in.close(); 38298944Sobrien 38398944Sobrien // Convert to a string 38498944Sobrien String allCharsString = new String(allChars); 38598944Sobrien return allCharsString; 38698944Sobrien } catch (FileNotFoundException e) { 38798944Sobrien System.err.println(e); 38898944Sobrien throw new Error("File not found: " + fileName); 38998944Sobrien } catch (IOException e) { 39098944Sobrien System.err.println(e); 39198944Sobrien throw new Error("Error reading file: " + fileName); 392130803Smarcel } 393130803Smarcel } 39498944Sobrien 39598944Sobrien /** 39698944Sobrien * Compare the two given files. 39798944Sobrien * 39898944Sobrien * @param file1 the first file to compare. 39998944Sobrien * @param file2 the second file to compare. 40098944Sobrien * @param throwErrorIFNoMatch flag to indicate whether or not to throw 40198944Sobrien * an error if the files do not match. 40298944Sobrien * @return true if the files are the same and false otherwise. 40398944Sobrien */ 40498944Sobrien public boolean diff(String file1, String file2, boolean throwErrorIFNoMatch) throws Error { 40598944Sobrien String file1Contents = readFileToString(file1); 40698944Sobrien String file2Contents = readFileToString(file2); 40798944Sobrien numTestsRun++; 40898944Sobrien if (file1Contents.trim().compareTo(file2Contents.trim()) == 0) { 40998944Sobrien System.out.println("Diff successful: " + file1 + ", " + file2); 41098944Sobrien numTestsPassed++; 41198944Sobrien return true; 41298944Sobrien } else if (throwErrorIFNoMatch) { 41398944Sobrien throw new Error("Diff failed: " + file1 + ", " + file2); 41498944Sobrien } else { 41598944Sobrien return false; 41698944Sobrien } 41798944Sobrien } 41898944Sobrien 41998944Sobrien /** 42098944Sobrien * Search for the string in the given file and return true 42198944Sobrien * if the string was found. 42298944Sobrien * 42398944Sobrien * @param fileString the contents of the file to search through 42498944Sobrien * @param stringToFind the string to search for 42598944Sobrien * @return true if the string was found 42698944Sobrien */ 42798944Sobrien private boolean findString(String fileString, String stringToFind) { 42898944Sobrien return fileString.indexOf(stringToFind) >= 0; 42998944Sobrien } 43098944Sobrien 431130803Smarcel /** 43298944Sobrien * Return the standard output. 43398944Sobrien * @return the standard output 434130803Smarcel */ 43598944Sobrien public String getStandardOutput() { 43698944Sobrien return standardOut.toString(); 43798944Sobrien } 43898944Sobrien 43998944Sobrien /** 44098944Sobrien * Return the error output. 44198944Sobrien * @return the error output 44298944Sobrien */ 44398944Sobrien public String getErrorOutput() { 44498944Sobrien return errors.getBuffer().toString(); 44598944Sobrien } 44698944Sobrien 44798944Sobrien /** 44898944Sobrien * Return the notice output. 44998944Sobrien * @return the notice output 45098944Sobrien */ 45198944Sobrien public String getNoticeOutput() { 45298944Sobrien return notices.getBuffer().toString(); 45398944Sobrien } 45498944Sobrien 45598944Sobrien /** 45698944Sobrien * Return the warning output. 45798944Sobrien * @return the warning output 45898944Sobrien */ 45998944Sobrien public String getWarningOutput() { 46098944Sobrien return warnings.getBuffer().toString(); 46198944Sobrien } 46298944Sobrien 46398944Sobrien /** 46498944Sobrien * A utility to copy a directory from one place to another. 46598944Sobrien * We may possibly want to move this to our doclet toolkit in 46698944Sobrien * the near future and maintain it from there. 46798944Sobrien * 46898944Sobrien * @param targetDir the directory to copy. 46998944Sobrien * @param destDir the destination to copy the directory to. 47098944Sobrien */ 47198944Sobrien public static void copyDir(String targetDir, String destDir) { 47298944Sobrien if (targetDir.endsWith("SCCS")) { 47398944Sobrien return; 47498944Sobrien } 47598944Sobrien try { 47698944Sobrien File targetDirObj = new File(targetDir); 47798944Sobrien File destDirParentObj = new File(destDir); 47898944Sobrien File destDirObj = new File(destDirParentObj, targetDirObj.getName()); 47998944Sobrien if (! destDirParentObj.exists()) { 48098944Sobrien destDirParentObj.mkdir(); 48198944Sobrien } 48298944Sobrien if (! destDirObj.exists()) { 48398944Sobrien destDirObj.mkdir(); 48498944Sobrien } 48598944Sobrien String[] files = targetDirObj.list(); 48698944Sobrien for (int i = 0; i < files.length; i++) { 48798944Sobrien File srcFile = new File(targetDirObj, files[i]); 48898944Sobrien File destFile = new File(destDirObj, files[i]); 48998944Sobrien if (srcFile.isFile()) { 49098944Sobrien System.out.println("Copying " + srcFile + " to " + destFile); 49198944Sobrien copyFile(destFile, srcFile); 49298944Sobrien } else if(srcFile.isDirectory()) { 49398944Sobrien copyDir(srcFile.getAbsolutePath(), destDirObj.getAbsolutePath()); 49498944Sobrien } 49598944Sobrien } 49698944Sobrien } catch (IOException exc) { 49798944Sobrien throw new Error("Could not copy " + targetDir + " to " + destDir); 49898944Sobrien } 49998944Sobrien } 50098944Sobrien 50198944Sobrien /** 50298944Sobrien * Copy source file to destination file. 50398944Sobrien * 50498944Sobrien * @throws SecurityException 50598944Sobrien * @throws IOException 50698944Sobrien */ 50798944Sobrien public static void copyFile(File destfile, File srcfile) 50898944Sobrien throws IOException { 50998944Sobrien byte[] bytearr = new byte[512]; 51098944Sobrien int len = 0; 51198944Sobrien FileInputStream input = new FileInputStream(srcfile); 51298944Sobrien File destDir = destfile.getParentFile(); 51398944Sobrien destDir.mkdirs(); 51498944Sobrien FileOutputStream output = new FileOutputStream(destfile); 51598944Sobrien try { 51698944Sobrien while ((len = input.read(bytearr)) != -1) { 51798944Sobrien output.write(bytearr, 0, len); 51898944Sobrien } 51998944Sobrien } catch (FileNotFoundException exc) { 52098944Sobrien } catch (SecurityException exc) { 52198944Sobrien } finally { 52298944Sobrien input.close(); 52398944Sobrien output.close(); 52498944Sobrien } 52598944Sobrien } 52698944Sobrien} 52798944Sobrien