| /* | 
 |  * Copyright 2006 Google Inc. | 
 |  *  | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
 |  * use this file except in compliance with the License. You may obtain a copy of | 
 |  * the License at | 
 |  *  | 
 |  * http://www.apache.org/licenses/LICENSE-2.0 | 
 |  *  | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
 |  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
 |  * License for the specific language governing permissions and limitations under | 
 |  * the License. | 
 |  */ | 
 | package com.google.doctool; | 
 |  | 
 | import org.xml.sax.Attributes; | 
 | import org.xml.sax.InputSource; | 
 | import org.xml.sax.SAXException; | 
 | import org.xml.sax.XMLReader; | 
 | import org.xml.sax.helpers.DefaultHandler; | 
 |  | 
 | import java.io.File; | 
 | import java.io.FileInputStream; | 
 | import java.io.FileOutputStream; | 
 | import java.io.FileReader; | 
 | import java.io.FileWriter; | 
 | import java.io.IOException; | 
 | import java.io.InputStream; | 
 | import java.io.OutputStream; | 
 | import java.io.PrintStream; | 
 | import java.io.StringReader; | 
 | import java.io.StringWriter; | 
 | import java.util.ArrayList; | 
 | import java.util.HashMap; | 
 | import java.util.HashSet; | 
 | import java.util.Iterator; | 
 | import java.util.List; | 
 | import java.util.Map; | 
 | import java.util.Set; | 
 | import java.util.regex.Matcher; | 
 | import java.util.regex.Pattern; | 
 |  | 
 | import javax.xml.parsers.ParserConfigurationException; | 
 | import javax.xml.parsers.SAXParser; | 
 | import javax.xml.parsers.SAXParserFactory; | 
 | import javax.xml.transform.Transformer; | 
 | import javax.xml.transform.TransformerConfigurationException; | 
 | import javax.xml.transform.TransformerException; | 
 | import javax.xml.transform.TransformerFactory; | 
 | import javax.xml.transform.stream.StreamResult; | 
 | import javax.xml.transform.stream.StreamSource; | 
 |  | 
 | /** | 
 |  * Orchestrates the behavior of {@link Booklet}, {@link SplitterJoiner} and | 
 |  * other tools to create user documentation and API documentation. | 
 |  */ | 
 | public class DocTool { | 
 |  | 
 |   private class ImageCopier extends DefaultHandler { | 
 |  | 
 |     private final File htmlDir; | 
 |  | 
 |     private ImageCopier(File htmlDir) { | 
 |       this.htmlDir = htmlDir; | 
 |     } | 
 |  | 
 |     public void startElement(String uri, String localName, String qName, | 
 |         Attributes attributes) throws SAXException { | 
 |       if (qName.equalsIgnoreCase("img")) { | 
 |         String imgSrc = attributes.getValue("src"); | 
 |         if (imgSrc != null) { | 
 |           boolean found = false; | 
 |           for (int i = 0, n = imagePath.length; i < n; ++i) { | 
 |             File dir = imagePath[i]; | 
 |             File inFile = new File(dir, imgSrc); | 
 |             if (inFile.exists()) { | 
 |               // Copy it over. | 
 |               // | 
 |               found = true; | 
 |               File outFile = new File(htmlDir, imgSrc); | 
 |  | 
 |               if (outFile.exists()) { | 
 |                 if (outFile.lastModified() > inFile.lastModified()) { | 
 |                   // Already up to date. | 
 |                   break; | 
 |                 } | 
 |               } else { | 
 |                 File outFileDir = outFile.getParentFile(); | 
 |                 if (!outFileDir.exists() && !outFileDir.mkdirs()) { | 
 |                   err.println("Unable to create image output dir " + outFileDir); | 
 |                   break; | 
 |                 } | 
 |               } | 
 |               if (!copyFile(inFile, outFile)) { | 
 |                 err.println("Unable to copy image file " + outFile); | 
 |               } | 
 |             } | 
 |           } | 
 |           if (!found) { | 
 |             err.println("Unable to find image " + imgSrc); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private static final Pattern IN_XML_FILENAME = Pattern.compile( | 
 |       "(.+)\\.([^\\.]+)\\.xml", Pattern.CASE_INSENSITIVE); | 
 |  | 
 |   public static void main(String[] args) { | 
 |     DocToolFactory factory = new DocToolFactory(); | 
 |     String arg; | 
 |     String pathSep = System.getProperty("path.separator"); | 
 |     for (int i = 0, n = args.length; i < n; ++i) { | 
 |       if (tryParseFlag(args, i, "-help")) { | 
 |         printHelp(); | 
 |         return; | 
 |       } else if (null != (arg = tryParseArg(args, i, "-out"))) { | 
 |         ++i; | 
 |         factory.setOutDir(arg); | 
 |       } else if (null != (arg = tryParseArg(args, i, "-html"))) { | 
 |         ++i; | 
 |         factory.setGenerateHtml(true); | 
 |         factory.setTitle(arg); | 
 |  | 
 |         // Slurp every arg not prefixed with "-". | 
 |         for (; i + 1 < n && !args[i + 1].startsWith("-"); ++i) { | 
 |           factory.addHtmlFileBase(args[i + 1]); | 
 |         } | 
 |       } else if (null != (arg = tryParseArg(args, i, "-overview"))) { | 
 |         ++i; | 
 |         factory.setOverviewFile(arg); | 
 |       } else if (null != (arg = tryParseArg(args, i, "-sourcepath"))) { | 
 |         ++i; | 
 |         String[] entries = arg.split("\\" + pathSep); | 
 |         for (int entryIndex = 0; entryIndex < entries.length; entryIndex++) { | 
 |           factory.addToSourcePath(entries[entryIndex]); | 
 |         } | 
 |       } else if (null != (arg = tryParseArg(args, i, "-classpath"))) { | 
 |         ++i; | 
 |         String[] entries = arg.split("\\" + pathSep); | 
 |         for (int entryIndex = 0; entryIndex < entries.length; entryIndex++) { | 
 |           factory.addToClassPath(entries[entryIndex]); | 
 |         } | 
 |       } else if (null != (arg = tryParseArg(args, i, "-packages"))) { | 
 |         ++i; | 
 |         String[] entries = arg.split("\\" + pathSep); | 
 |         for (int entryIndex = 0; entryIndex < entries.length; entryIndex++) { | 
 |           factory.addToPackages(entries[entryIndex]); | 
 |         } | 
 |       } else if (null != (arg = tryParseArg(args, i, "-imagepath"))) { | 
 |         ++i; | 
 |         String[] entries = arg.split("\\" + pathSep); | 
 |         for (int entryIndex = 0; entryIndex < entries.length; entryIndex++) { | 
 |           factory.addToImagePath(entries[entryIndex]); | 
 |         } | 
 |       } else { | 
 |         if (factory.getFileType() == null) { | 
 |           factory.setFileType(args[i]); | 
 |         } else { | 
 |           factory.setFileBase(args[i]); | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     DocTool docTool = factory.create(System.out, System.err); | 
 |     if (docTool != null) { | 
 |       docTool.process(); | 
 |     } | 
 |   } | 
 |  | 
 |   public static boolean recursiveDelete(File file) { | 
 |     if (file.isDirectory()) { | 
 |       File[] children = file.listFiles(); | 
 |       if (children != null) { | 
 |         for (int i = 0; i < children.length; i++) { | 
 |           if (!recursiveDelete(children[i])) { | 
 |             return false; | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |     if (!file.delete()) { | 
 |       System.err.println("Unable to delete " + file.getAbsolutePath()); | 
 |       return false; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   private static void printHelp() { | 
 |     String s = ""; | 
 |     s += "DocTool (filetype filebase)? [docset-creation-options] [html-creation-options]\n"; | 
 |     s += "    Creates structured javadoc xml output from Java source and/or\n"; | 
 |     s += "    a table of contents and a set of cross-linked html files.\n"; | 
 |     s += "    Specifying filebase/filetype produces output file \"filebase.filetype.xml\".\n"; | 
 |     s += "    Specifying -html produces output files in ${out}/html.\n"; | 
 |     s += "\n"; | 
 |     s += "[docset-creation-options] are\n"; | 
 |     s += "  -out\n"; | 
 |     s += "    The output directory\n"; | 
 |     s += "  -overview\n"; | 
 |     s += "    The overview html file for this doc set\n"; | 
 |     s += "  -sourcepath path\n"; | 
 |     s += "    The path to find Java source for this doc set\n"; | 
 |     s += "  -classpath path\n"; | 
 |     s += "    The path to find imported classes for this doc set\n"; | 
 |     s += "  -packages package-names\n"; | 
 |     s += "    The command-separated list of package names to include in this doc set\n"; | 
 |     s += "\n"; | 
 |     s += "[html-creation-options] are\n"; | 
 |     s += "  -html title filebase+\n"; | 
 |     s += "    Causes topics in the named filebase(s) to be merged and converted into html\n"; | 
 |     s += "  -imagepath\n"; | 
 |     s += "    The semicolon-separated path to find images for html\n"; | 
 |     System.out.println(s); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Parse a flag with a argument. | 
 |    */ | 
 |   private static String tryParseArg(String[] args, int i, String name) { | 
 |     if (i < args.length) { | 
 |       if (args[i].equals(name)) { | 
 |         if (i + 1 < args.length) { | 
 |           String arg = args[i + 1]; | 
 |           if (arg.startsWith("-")) { | 
 |             System.out.println("Warning: arg to " + name | 
 |                 + " looks more like a flag: " + arg); | 
 |           } | 
 |           return arg; | 
 |         } else { | 
 |           throw new IllegalArgumentException("Expecting an argument after " | 
 |               + name); | 
 |         } | 
 |       } | 
 |     } | 
 |     return null; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Parse just a flag with no subsequent argument. | 
 |    */ | 
 |   private static boolean tryParseFlag(String[] args, int i, String name) { | 
 |     if (i < args.length) { | 
 |       if (args[i].equals(name)) { | 
 |         return true; | 
 |       } | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   private final File[] classPath; | 
 |  | 
 |   private final String[] packages; | 
 |  | 
 |   private final PrintStream err; | 
 |  | 
 |   private final String base; | 
 |  | 
 |   private final String fileType; | 
 |  | 
 |   private final boolean generateHtml; | 
 |  | 
 |   private final String[] htmlFileBases; | 
 |  | 
 |   private final File[] imagePath; | 
 |  | 
 |   private final PrintStream out; | 
 |  | 
 |   private final File outDir; | 
 |  | 
 |   private final File overviewFile; | 
 |  | 
 |   private final File[] sourcePath; | 
 |  | 
 |   private final String title; | 
 |  | 
 |   DocTool(PrintStream out, PrintStream err, File outDir, boolean generateHtml, | 
 |       String title, String[] htmlFileBases, String fileType, String fileBase, | 
 |       File overviewFile, File[] sourcePath, File[] classPath, | 
 |       String[] packages, File[] imagePath) { | 
 |     this.out = out; | 
 |     this.err = err; | 
 |     this.outDir = outDir; | 
 |     this.generateHtml = generateHtml; | 
 |     this.base = fileBase; | 
 |     this.fileType = fileType; | 
 |     this.overviewFile = overviewFile; | 
 |     this.sourcePath = sourcePath; | 
 |     this.classPath = classPath; | 
 |     this.packages = packages; | 
 |     this.imagePath = imagePath; | 
 |     this.title = title; | 
 |     this.htmlFileBases = htmlFileBases.clone(); | 
 |   } | 
 |  | 
 |   public boolean copyFile(File in, File out) { | 
 |     FileInputStream fis = null; | 
 |     FileOutputStream fos = null; | 
 |     try { | 
 |       fis = new FileInputStream(in); | 
 |       fos = new FileOutputStream(out); | 
 |       byte[] buf = new byte[4096]; | 
 |       int i = 0; | 
 |       while ((i = fis.read(buf)) != -1) { | 
 |         fos.write(buf, 0, i); | 
 |       } | 
 |       return true; | 
 |     } catch (Exception e) { | 
 |       return false; | 
 |     } finally { | 
 |       close(fis); | 
 |       close(fos); | 
 |     } | 
 |   } | 
 |  | 
 |   private void close(InputStream is) { | 
 |     if (is != null) { | 
 |       try { | 
 |         is.close(); | 
 |       } catch (IOException e) { | 
 |         e.printStackTrace(err); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void close(OutputStream os) { | 
 |     if (os != null) { | 
 |       try { | 
 |         os.close(); | 
 |       } catch (IOException e) { | 
 |         e.printStackTrace(err); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private boolean copyImages(File htmlDir, File mergedTopicsFile) { | 
 |     FileReader fileReader = null; | 
 |     Throwable caught = null; | 
 |     try { | 
 |       fileReader = new FileReader(mergedTopicsFile); | 
 |       SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); | 
 |       InputSource inputSource = new InputSource(fileReader); | 
 |       XMLReader xmlReader = parser.getXMLReader(); | 
 |       xmlReader.setContentHandler(new ImageCopier(htmlDir)); | 
 |       xmlReader.parse(inputSource); | 
 |       return true; | 
 |     } catch (SAXException e) { | 
 |       caught = e; | 
 |       Exception inner = e.getException(); | 
 |       if (inner != null) { | 
 |         caught = inner; | 
 |       } | 
 |     } catch (ParserConfigurationException e) { | 
 |       caught = e; | 
 |     } catch (IOException e) { | 
 |       caught = e; | 
 |     } finally { | 
 |       try { | 
 |         if (fileReader != null) { | 
 |           fileReader.close(); | 
 |         } | 
 |       } catch (IOException e) { | 
 |         e.printStackTrace(err); | 
 |       } | 
 |     } | 
 |     caught.printStackTrace(err); | 
 |     return false; | 
 |   } | 
 |  | 
 |   private Set findSourcePackages() { | 
 |     Set results = new HashSet(); | 
 |     for (int i = 0, n = sourcePath.length; i < n; ++i) { | 
 |       File srcDir = sourcePath[i]; | 
 |       findSourcePackages(results, srcDir, ""); | 
 |     } | 
 |     return results; | 
 |   } | 
 |  | 
 |   private void findSourcePackages(Set results, File dir, String parentPackage) { | 
 |     File[] children = dir.listFiles(); | 
 |     if (children != null) { | 
 |       for (int i = 0, n = children.length; i < n; ++i) { | 
 |         File child = children[i]; | 
 |         String childName = parentPackage | 
 |             + (parentPackage.length() > 0 ? "." : "") + child.getName(); | 
 |         if (child.isDirectory()) { | 
 |           // Recurse | 
 |           findSourcePackages(results, child, childName); | 
 |         } else if (child.getName().endsWith(".java")) { | 
 |           // Only include this dir as a result if there's at least one java file | 
 |           results.add(parentPackage); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private String flattenPath(File[] entries) { | 
 |     String pathSep = System.getProperty("path.separator"); | 
 |     String path = ""; | 
 |     for (int i = 0, n = entries.length; i < n; ++i) { | 
 |       File entry = entries[i]; | 
 |       if (i > 0) { | 
 |         path += pathSep; | 
 |       } | 
 |       path += entry.getAbsolutePath(); | 
 |     } | 
 |     return path; | 
 |   } | 
 |  | 
 |   private void freshenIf(File file) { | 
 |     if (!file.isFile()) { | 
 |       return; | 
 |     } | 
 |  | 
 |     String name = file.getName(); | 
 |     Matcher matcher = IN_XML_FILENAME.matcher(name); | 
 |     if (matcher.matches()) { | 
 |       String suffix = "." + matcher.group(2) + ".xml"; | 
 |       File topicFile = tryReplaceSuffix(file, suffix, ".topics.xml"); | 
 |       if (topicFile != null) { | 
 |         if (file.lastModified() > topicFile.lastModified()) { | 
 |           String xsltFileName = matcher.group(2) + "-" + "topics.xslt"; | 
 |           String xslt = getFileFromClassPath(xsltFileName); // yucky slow | 
 |           out.println(file + " -> " + topicFile); | 
 |           transform(xslt, file, topicFile, null); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private boolean genHtml() { | 
 |     // Make sure the html directory exists. | 
 |     // | 
 |     File htmlDir = new File(outDir, "html"); | 
 |     if (!htmlDir.exists() && !htmlDir.mkdirs()) { | 
 |       err.println("Cannot create html output directory " | 
 |           + htmlDir.getAbsolutePath()); | 
 |       return false; | 
 |     } | 
 |  | 
 |     // Merge all *.topics.xml into one topics.xml file. | 
 |     // | 
 |     File mergedTopicsFile = new File(outDir, "topics.xml"); | 
 |     if (!mergeTopics(mergedTopicsFile)) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     // Parse it all to find the images and copy them over. | 
 |     // | 
 |     copyImages(htmlDir, mergedTopicsFile); | 
 |  | 
 |     // Transform to merged topics into merged htmls. | 
 |     // | 
 |     File mergedHtmlsFile = new File(htmlDir, "topics.htmls"); | 
 |     long lastModifiedHtmls = mergedHtmlsFile.lastModified(); | 
 |     long lastModifiedTopics = mergedTopicsFile.lastModified(); | 
 |     if (!mergedHtmlsFile.exists() || lastModifiedHtmls < lastModifiedTopics) { | 
 |       String xsltHtmls = getFileFromClassPath("topics-htmls.xslt"); | 
 |  | 
 |       Map params = new HashMap(); | 
 |       params.put("title", title); | 
 |  | 
 |       transform(xsltHtmls, mergedTopicsFile, mergedHtmlsFile, params); | 
 |  | 
 |       // Split the merged htmls into many html files. | 
 |       // | 
 |       if (!splitHtmls(mergedHtmlsFile)) { | 
 |         return false; | 
 |       } | 
 |  | 
 |       // Create a table of contents. | 
 |       // | 
 |       File tocFile = new File(htmlDir, "contents.html"); | 
 |       String xsltToc = getFileFromClassPath("topics-toc.xslt"); | 
 |       transform(xsltToc, mergedTopicsFile, tocFile, params); | 
 |  | 
 |       // Copy the CSS file over. | 
 |       // | 
 |       String css = getFileFromClassPath("doc.css"); | 
 |       try { | 
 |         FileWriter cssWriter = new FileWriter(new File(htmlDir, "doc.css")); | 
 |         cssWriter.write(css); | 
 |         cssWriter.close(); | 
 |       } catch (IOException e) { | 
 |         e.printStackTrace(err); | 
 |       } | 
 |     } else { | 
 |       out.println("Skipping html creation since nothing seems to have changed since " | 
 |           + mergedHtmlsFile.getAbsolutePath()); | 
 |     } | 
 |  | 
 |     return true; | 
 |   } | 
 |  | 
 |   private String getFileFromClassPath(String filename) { | 
 |     InputStream in = null; | 
 |     try { | 
 |       in = getClass().getClassLoader().getResourceAsStream(filename); | 
 |       try { | 
 |         if (in == null) { | 
 |           err.println("Cannot find file: " + filename); | 
 |           System.exit(-1); // yuck | 
 |         } | 
 |         StringWriter sw = new StringWriter(); | 
 |         int ch; | 
 |         while ((ch = in.read()) != -1) { | 
 |           sw.write(ch); | 
 |         } | 
 |         return sw.toString(); | 
 |       } finally { | 
 |         if (in != null) { | 
 |           in.close(); | 
 |         } | 
 |       } | 
 |     } catch (IOException e) { | 
 |       throw new RuntimeException(e); | 
 |     } | 
 |   } | 
 |  | 
 |   private boolean mergeTopics(File mergedTopicsFile) { | 
 |     try { | 
 |       List args = new ArrayList(); | 
 |       args.add("join"); // what to do | 
 |       args.add("topics"); // the outer element is <topics> | 
 |       args.add(mergedTopicsFile.getAbsolutePath()); | 
 |  | 
 |       // For each of the htmlFileBases, try to find a file having that name to | 
 |       // merge into the big topics doc. | 
 |       // | 
 |       boolean foundAny = false; | 
 |       for (int i = 0, n = htmlFileBases.length; i < n; ++i) { | 
 |         String filebase = htmlFileBases[i]; | 
 |         File fileToMerge = new File(outDir, filebase + ".topics.xml"); | 
 |         if (fileToMerge.exists()) { | 
 |           foundAny = true; | 
 |           args.add(fileToMerge.getAbsolutePath()); | 
 |         } else { | 
 |           err.println("Unable to find " + fileToMerge.getName()); | 
 |         } | 
 |       } | 
 |  | 
 |       if (foundAny) { | 
 |         String[] argArray = (String[]) args.toArray(new String[0]); | 
 |         traceCommand("SplitterJoiner", argArray); | 
 |         SplitterJoiner.main(argArray); | 
 |       } else { | 
 |         err.println("No topics found"); | 
 |         return false; | 
 |       } | 
 |     } catch (IOException e) { | 
 |       e.printStackTrace(err); | 
 |       return false; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Runs the help process. | 
 |    */ | 
 |   private boolean process() { | 
 |     if (fileType != null) { | 
 |       // Produce XML from JavaDoc. | 
 |       // | 
 |       String fileName = base + "." + fileType + ".xml"; | 
 |       if (!runBooklet(new File(outDir, fileName))) { | 
 |         return false; | 
 |       } | 
 |     } | 
 |  | 
 |     // Process existing files to get them into topics format. | 
 |     // Done afterwards for convenience when debugging your doc. | 
 |     // | 
 |     transformExistingIntoTopicXml(); | 
 |  | 
 |     if (generateHtml) { | 
 |       // Merge into HTML. | 
 |       if (!genHtml()) { | 
 |         return false; | 
 |       } | 
 |     } | 
 |  | 
 |     return true; | 
 |   } | 
 |  | 
 |   private boolean runBooklet(File bkoutFile) { | 
 |     // Write out the list of packages that can be found on the source path. | 
 |     out.println("Creating " + bkoutFile.getAbsolutePath()); | 
 |     Set srcPackages = findSourcePackages(); | 
 |     if (srcPackages.isEmpty()) { | 
 |       err.println("No input files found"); | 
 |       return false; | 
 |     } | 
 |  | 
 |     List args = new ArrayList(); | 
 |  | 
 |     // For now, harded-coded, but could be passed through | 
 |     args.add("-source"); | 
 |     args.add("1.5"); | 
 |  | 
 |     // The doclet | 
 |     args.add("-doclet"); | 
 |     args.add(Booklet.class.getName()); | 
 |  | 
 |     // Class path | 
 |     args.add("-classpath"); | 
 |     args.add(flattenPath(classPath)); | 
 |  | 
 |     // Source path | 
 |     args.add("-sourcepath"); | 
 |     args.add(flattenPath(sourcePath)); | 
 |      | 
 |     // Encoding is always UTF-8 | 
 |     args.add("-encoding"); | 
 |     args.add("UTF-8"); | 
 |  | 
 |     // Overview file | 
 |     if (overviewFile != null) { | 
 |       args.add("-overview"); | 
 |       args.add(overviewFile.getAbsolutePath()); | 
 |     } | 
 |  | 
 |     // Output file | 
 |     args.add("-bkout"); | 
 |     args.add(bkoutFile.getAbsolutePath()); | 
 |  | 
 |     if (packages != null) { | 
 |       // Specify the packages to actually emit doc for | 
 |       StringBuffer bkdocpkg = new StringBuffer(); | 
 |       for (int i = 0; i < packages.length; i++) { | 
 |         String pkg = packages[i]; | 
 |         bkdocpkg.append(pkg); | 
 |         bkdocpkg.append(";"); | 
 |       } | 
 |       args.add("-bkdocpkg"); | 
 |       args.add(bkdocpkg.toString()); | 
 |     } | 
 |  | 
 |     args.add("-breakiterator"); | 
 |  | 
 |     // Specify the set of input packages (needed by JavaDoc) | 
 |     args.addAll(srcPackages); | 
 |  | 
 |     String[] argArray = (String[]) args.toArray(new String[0]); | 
 |     traceCommand("Booklet", argArray); | 
 |     Booklet.main(argArray); | 
 |  | 
 |     return bkoutFile.exists(); | 
 |   } | 
 |  | 
 |   private boolean splitHtmls(File mergedHtmlsFile) { | 
 |     try { | 
 |       List args = new ArrayList(); | 
 |       args.add("split"); // what to do | 
 |       args.add(mergedHtmlsFile.getAbsolutePath()); | 
 |       String[] argArray = (String[]) args.toArray(new String[0]); | 
 |       traceCommand("SplitterJoiner", argArray); | 
 |       SplitterJoiner.main(argArray); | 
 |     } catch (IOException e) { | 
 |       e.printStackTrace(err); | 
 |       return false; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   private void traceCommand(String cmd, String[] args) { | 
 |     out.print(cmd); | 
 |     for (int i = 0, n = args.length; i < n; ++i) { | 
 |       String arg = args[i]; | 
 |       out.print(" "); | 
 |       out.print(arg); | 
 |     } | 
 |     out.println(); | 
 |   } | 
 |  | 
 |   private void transform(String xslt, File inFile, File outFile, Map params) { | 
 |     Throwable caught = null; | 
 |     try { | 
 |       TransformerFactory transformerFactory = TransformerFactory.newInstance(); | 
 |       StreamSource xsltSource = new StreamSource(new StringReader(xslt)); | 
 |       Transformer transformer = transformerFactory.newTransformer(xsltSource); | 
 |       transformer.setOutputProperty( | 
 |           javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); | 
 |       transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, | 
 |           "yes"); | 
 |       transformer.setOutputProperty( | 
 |           "{http://xml.apache.org/xslt}indent-amount", "4"); | 
 |  | 
 |       if (params != null) { | 
 |         for (Iterator iter = params.entrySet().iterator(); iter.hasNext();) { | 
 |           Map.Entry entry = (Map.Entry) iter.next(); | 
 |           transformer.setParameter((String) entry.getKey(), entry.getValue()); | 
 |         } | 
 |       } | 
 |  | 
 |       FileOutputStream fos = new FileOutputStream(outFile); | 
 |       StreamResult result = new StreamResult(fos); | 
 |       StreamSource xmlSource = new StreamSource(new FileReader(inFile)); | 
 |       transformer.transform(xmlSource, result); | 
 |       fos.close(); | 
 |       return; | 
 |     } catch (TransformerConfigurationException e) { | 
 |       caught = e; | 
 |     } catch (TransformerException e) { | 
 |       caught = e; | 
 |     } catch (IOException e) { | 
 |       caught = e; | 
 |     } | 
 |     throw new RuntimeException("Unable to complete the xslt tranform", caught); | 
 |   } | 
 |  | 
 |   private void transformExistingIntoTopicXml() { | 
 |     File[] children = outDir.listFiles(); | 
 |     if (children != null) { | 
 |       for (int i = 0, n = children.length; i < n; ++i) { | 
 |         File file = children[i]; | 
 |         freshenIf(file); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private File tryReplaceSuffix(File file, String oldSuffix, String newSuffix) { | 
 |     String name = file.getName(); | 
 |     if (name.endsWith(oldSuffix)) { | 
 |       String baseName = name.substring(0, name.length() - oldSuffix.length()); | 
 |       return new File(file.getParent(), baseName + newSuffix); | 
 |     } else { | 
 |       return null; | 
 |     } | 
 |   } | 
 | } |