8001664: refactor javadoc to use abstraction to handle files
authorjjg
Wed, 31 Oct 2012 13:48:15 -0700
changeset 14368 6f4c62de6985
parent 14367 c890ac2d9641
child 14369 3d660d08d1f7
8001664: refactor javadoc to use abstraction to handle files Reviewed-by: darcy
langtools/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java
langtools/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java
langtools/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java
langtools/src/share/classes/com/sun/tools/doclets/formats/html/SourceToHTMLConverter.java
langtools/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AnnotationTypeBuilder.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/ClassBuilder.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/PackageSummaryBuilder.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFile.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocPaths.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Extern.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/PackageListWriter.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/SourcePath.java
langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Util.java
langtools/test/com/sun/javadoc/testDocFileDir/TestDocFileDir.java
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java	Wed Oct 31 13:48:15 2012 -0700
@@ -25,7 +25,6 @@
 
 package com.sun.tools.doclets.formats.html;
 
-import java.io.*;
 import java.net.*;
 import java.util.*;
 
@@ -46,6 +45,11 @@
  * use "-helpfile" option when already "-nohelp" option is used.
  * </p>
  *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ *
  * @author Robert Field.
  * @author Atul Dambalkar.
  * @author Jamie Ho
@@ -362,7 +366,7 @@
                         "-helpfile"));
                     return false;
                 }
-                File help = new File(os[1]);
+                DocFile help = DocFile.createFileForInput(this, os[1]);
                 if (!help.exists()) {
                     reporter.printError(getText("doclet.File_not_found", os[1]));
                     return false;
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java	Wed Oct 31 13:48:15 2012 -0700
@@ -104,15 +104,12 @@
             return;
         }
         boolean nodeprecated = configuration.nodeprecated;
-        String configdestdir = configuration.destDirName;
-        String confighelpfile = configuration.helpfile;
-        String configstylefile = configuration.stylesheetfile;
-        performCopy(configdestdir, confighelpfile);
-        performCopy(configdestdir, configstylefile);
-        Util.copyResourceFile(configuration, "background.gif", false);
-        Util.copyResourceFile(configuration, "tab.gif", false);
-        Util.copyResourceFile(configuration, "titlebar.gif", false);
-        Util.copyResourceFile(configuration, "titlebar_end.gif", false);
+        performCopy(configuration.helpfile);
+        performCopy(configuration.stylesheetfile);
+        copyResourceFile("background.gif");
+        copyResourceFile("tab.gif");
+        copyResourceFile("titlebar.gif");
+        copyResourceFile("titlebar_end.gif");
         // do early to reduce memory footprint
         if (configuration.classuse) {
             ClassUseWriter.generate(configuration, classtree);
@@ -149,8 +146,8 @@
         // If a stylesheet file is not specified, copy the default stylesheet
         // and replace newline with platform-specific newline.
         if (configuration.stylesheetfile.length() == 0) {
-            Util.copyFile(configuration, "stylesheet.css", DocPaths.RESOURCES,
-                    DocPath.empty, false, true);
+            DocFile f = DocFile.createFileForOutput(configuration, DocPaths.STYLESHEET);
+            f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.STYLESHEET), false, true);
         }
     }
 
@@ -251,29 +248,33 @@
         return (ConfigurationImpl.getInstance()).validOptions(options, reporter);
     }
 
-    private void performCopy(String configdestdir, String filename) {
+    /**
+     * Copy a file in the resources directory to the destination directory.
+     * @param resource   The name of the resource file to copy
+     */
+    private void copyResourceFile(String resource) {
+        DocPath p = DocPaths.RESOURCES.resolve(resource);
+        DocFile f = DocFile.createFileForOutput(configuration, p);
+        f.copyResource(p, false, false);
+    }
+
+    private void performCopy(String filename) {
+        if (filename.isEmpty())
+            return;
+
         try {
-            String destdir = (configdestdir.length() > 0) ?
-                configdestdir + File.separatorChar: "";
-            if (filename.length() > 0) {
-                File helpstylefile = new File(filename);
-                String parent = helpstylefile.getParent();
-                String helpstylefilename = (parent == null)?
-                    filename:
-                    filename.substring(parent.length() + 1);
-                File desthelpfile = new File(destdir + helpstylefilename);
-                if (!desthelpfile.getCanonicalPath().equals(
-                        helpstylefile.getCanonicalPath())) {
-                    configuration.message.
-                        notice((SourcePosition) null,
-                            "doclet.Copying_File_0_To_File_1",
-                            helpstylefile.toString(), desthelpfile.toString());
-                    Util.copyFile(desthelpfile, helpstylefile);
-                }
-            }
+            DocFile fromfile = DocFile.createFileForInput(configuration, filename);
+            DocPath path = DocPath.create(fromfile.getName());
+            DocFile toFile = DocFile.createFileForOutput(configuration, path);
+            if (toFile.isSameFile(fromfile))
+                return;
+
+            configuration.message.notice((SourcePosition) null,
+                    "doclet.Copying_File_0_To_File_1",
+                    fromfile.toString(), path.getPath());
+            toFile.copyFile(fromfile);
         } catch (IOException exc) {
-            configuration.message.
-                error((SourcePosition) null,
+            configuration.message.error((SourcePosition) null,
                     "doclet.perform_copy_exception_encountered",
                     exc.toString());
             throw new DocletAbortException();
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java	Wed Oct 31 13:48:15 2012 -0700
@@ -723,7 +723,8 @@
         if (helpfile.isEmpty()) {
             helpfilenm = DocPaths.HELP_DOC;
         } else {
-            helpfilenm = DocPath.create(new File(helpfile).getName());
+            DocFile file = DocFile.createFileForInput(configuration, helpfile);
+            helpfilenm = DocPath.create(file.getName());
         }
         Content linkContent = getHyperLink(pathToRoot.resolve(helpfilenm),
                 helpLabel, "", "");
@@ -1671,12 +1672,13 @@
      * @return an HtmlTree for the lINK tag which provides the stylesheet location
      */
     public HtmlTree getStyleSheetProperties() {
-        String filename = configuration.stylesheetfile;
+        String stylesheetfile = configuration.stylesheetfile;
         DocPath stylesheet;
-        if (filename.length() > 0) {
-            stylesheet = DocPath.create(new File(filename).getName());
+        if (stylesheetfile.isEmpty()) {
+            stylesheet = DocPaths.STYLESHEET;
         } else {
-            stylesheet = DocPaths.STYLESHEET;
+            DocFile file = DocFile.createFileForInput(configuration, stylesheetfile);
+            stylesheet = DocPath.create(file.getName());
         }
         HtmlTree link = HtmlTree.LINK("stylesheet", "text/css",
                 pathToRoot.resolve(stylesheet).getPath(),
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/SourceToHTMLConverter.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/SourceToHTMLConverter.java	Wed Oct 31 13:48:15 2012 -0700
@@ -26,11 +26,13 @@
 package com.sun.tools.doclets.formats.html;
 
 import java.io.*;
+
 import javax.tools.FileObject;
+
 import com.sun.javadoc.*;
+import com.sun.tools.doclets.formats.html.markup.*;
 import com.sun.tools.doclets.internal.toolkit.*;
 import com.sun.tools.doclets.internal.toolkit.util.*;
-import com.sun.tools.doclets.formats.html.markup.*;
 
 /**
  * Converts Java Source Code to HTML.
@@ -95,8 +97,7 @@
             // package files to HTML.
             if (!(configuration.nodeprecated &&
                     (Util.isDeprecated(cds[i]) || Util.isDeprecated(cds[i].containingPackage()))))
-                convertClass(configuration, cds[i],
-                        getPackageOutputDir(outputdir, cds[i].containingPackage()));
+                convertClass(configuration, cds[i], outputdir);
         }
     }
 
@@ -109,10 +110,9 @@
      */
     public static void convertPackage(ConfigurationImpl configuration, PackageDoc pd,
             DocPath outputdir) {
-        if (pd == null || outputdir == null) {
+        if (pd == null) {
             return;
         }
-        DocPath classOutputdir = getPackageOutputDir(outputdir, pd);
         ClassDoc[] cds = pd.allClasses();
         for (int i = 0; i < cds.length; i++) {
             // If -nodeprecated option is set and the class is marked as deprecated,
@@ -120,22 +120,11 @@
             // containing package deprecation since it is already check in
             // the calling method above.
             if (!(configuration.nodeprecated && Util.isDeprecated(cds[i])))
-                convertClass(configuration, cds[i], classOutputdir);
+                convertClass(configuration, cds[i], outputdir);
         }
     }
 
     /**
-     * Return the directory write output to for the given package.
-     *
-     * @param outputDir the directory to output to.
-     * @param pd the Package to generate output for.
-     * @return the package output directory as a String.
-     */
-    private static DocPath getPackageOutputDir(DocPath outputDir, PackageDoc pd) {
-        return outputDir.resolve(DocPath.forPackage(pd));
-    }
-
-    /**
      * Convert the given Class to an HTML.
      *
      * @param configuration the configuration.
@@ -144,7 +133,7 @@
      */
     public static void convertClass(ConfigurationImpl configuration, ClassDoc cd,
             DocPath outputdir) {
-        if (cd == null || outputdir == null) {
+        if (cd == null) {
             return;
         }
         try {
@@ -184,8 +173,8 @@
             addBlankLines(pre);
             Content div = HtmlTree.DIV(HtmlStyle.sourceContainer, pre);
             body.addContent(div);
-            writeToFile(body, outputdir, cd.name(), configuration);
-        } catch (Exception e){
+            writeToFile(body, outputdir.resolve(DocPath.forClass(cd)), configuration);
+        } catch (IOException e) {
             e.printStackTrace();
         }
     }
@@ -194,12 +183,11 @@
      * Write the output to the file.
      *
      * @param body the documentation content to be written to the file.
-     * @param outputDir the directory to output to.
-     * @param className the name of the class that I am converting to HTML.
+     * @param path the path for the file.
      * @param configuration the Doclet configuration to pass notices to.
      */
-    private static void writeToFile(Content body, DocPath outputDir,
-            String className, ConfigurationImpl configuration) throws IOException {
+    private static void writeToFile(Content body, DocPath path,
+            ConfigurationImpl configuration) throws IOException {
         Content htmlDocType = DocType.Transitional();
         Content head = new HtmlTree(HtmlTag.HEAD);
         head.addContent(HtmlTree.TITLE(new StringContent(
@@ -208,15 +196,15 @@
         Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
                 head, body);
         Content htmlDocument = new HtmlDocument(htmlDocType, htmlTree);
-        File dir = outputDir.resolveAgainst(configuration.destDirName);
-        dir.mkdirs();
-        File newFile = new File(dir, className + ".html");
-        configuration.message.notice("doclet.Generating_0", newFile.getPath());
-        FileOutputStream fout = new FileOutputStream(newFile);
-        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fout));
-        bw.write(htmlDocument.toString());
-        bw.close();
-        fout.close();
+        configuration.message.notice("doclet.Generating_0", path.getPath());
+        DocFile df = DocFile.createFileForOutput(configuration, path);
+        Writer w = df.openWriter();
+        try {
+            htmlDocument.write(w, true);
+        } finally {
+            w.close();
+        }
+
     }
 
     /**
@@ -229,7 +217,8 @@
         String filename = configuration.stylesheetfile;
         DocPath stylesheet;
         if (filename.length() > 0) {
-            stylesheet = DocPath.create(new File(filename).getName());
+            DocFile file = DocFile.createFileForInput(configuration, filename);
+            stylesheet = DocPath.create(file.getName());
         } else {
             stylesheet = DocPaths.STYLESHEET;
         }
--- a/langtools/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java	Wed Oct 31 13:48:15 2012 -0700
@@ -156,7 +156,7 @@
      */
     public HtmlWriter(Configuration configuration, DocPath path)
             throws IOException, UnsupportedEncodingException {
-        writer = Util.genWriter(configuration, path);
+        writer = DocFile.createFileForOutput(configuration, path).openWriter();
         this.configuration = configuration;
         this.memberDetailsListPrinted = false;
         packageTableHeader = new String[] {
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java	Wed Oct 31 13:48:15 2012 -0700
@@ -106,7 +106,7 @@
 
     /**
      * Start the generation of files. Call generate methods in the individual
-     * writers, which will in turn genrate the documentation files. Call the
+     * writers, which will in turn generate the documentation files. Call the
      * TreeWriter generation first to ensure the Class Hierarchy is built
      * first and then can be used in the later generation.
      *
@@ -124,17 +124,7 @@
         ClassTree classtree = new ClassTree(configuration, configuration.nodeprecated);
 
         generateClassFiles(root, classtree);
-        if (configuration.sourcepath != null && configuration.sourcepath.length() > 0) {
-            StringTokenizer pathTokens = new StringTokenizer(configuration.sourcepath,
-                String.valueOf(File.pathSeparatorChar));
-            boolean first = true;
-            while(pathTokens.hasMoreTokens()){
-                Util.copyDocFiles(configuration,
-                    new File(pathTokens.nextToken()),
-                    DocPaths.DOC_FILES, first);
-                first = false;
-            }
-        }
+        Util.copyDocFiles(configuration, DocPaths.DOC_FILES);
 
         PackageListWriter.generate(configuration);
         generatePackageFiles(classtree);
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/Configuration.java	Wed Oct 31 13:48:15 2012 -0700
@@ -456,7 +456,7 @@
                 tagletManager.addCustomTag(args[1], tagletpath);
                 continue;
             }
-            String[] tokens = Util.tokenize(args[1],
+            String[] tokens = tokenize(args[1],
                 TagletManager.SIMPLE_TAGLET_OPT_SEPERATOR, 3);
             if (tokens.length == 1) {
                 String tagName = args[1];
@@ -480,6 +480,47 @@
         }
     }
 
+    /**
+     * Given a string, return an array of tokens.  The separator can be escaped
+     * with the '\' character.  The '\' character may also be escaped by the
+     * '\' character.
+     *
+     * @param s         the string to tokenize.
+     * @param separator the separator char.
+     * @param maxTokens the maximum number of tokens returned.  If the
+     *                  max is reached, the remaining part of s is appended
+     *                  to the end of the last token.
+     *
+     * @return an array of tokens.
+     */
+    private String[] tokenize(String s, char separator, int maxTokens) {
+        List<String> tokens = new ArrayList<String>();
+        StringBuilder  token = new StringBuilder ();
+        boolean prevIsEscapeChar = false;
+        for (int i = 0; i < s.length(); i += Character.charCount(i)) {
+            int currentChar = s.codePointAt(i);
+            if (prevIsEscapeChar) {
+                // Case 1:  escaped character
+                token.appendCodePoint(currentChar);
+                prevIsEscapeChar = false;
+            } else if (currentChar == separator && tokens.size() < maxTokens-1) {
+                // Case 2:  separator
+                tokens.add(token.toString());
+                token = new StringBuilder();
+            } else if (currentChar == '\\') {
+                // Case 3:  escape character
+                prevIsEscapeChar = true;
+            } else {
+                // Case 4:  regular character
+                token.appendCodePoint(currentChar);
+            }
+        }
+        if (token.length() > 0) {
+            tokens.add(token.toString());
+        }
+        return tokens.toArray(new String[] {});
+    }
+
     private void addToSet(Set<String> s, String str){
         StringTokenizer st = new StringTokenizer(str, ":");
         String current;
@@ -532,12 +573,12 @@
             String opt = os[0].toLowerCase();
             if (opt.equals("-d")) {
                 String destdirname = addTrailingFileSep(os[1]);
-                File destDir = new File(destdirname);
+                DocFile destDir = DocFile.createFileForDirectory(this, destdirname);
                 if (!destDir.exists()) {
                     //Create the output directory (in case it doesn't exist yet)
                     reporter.printNotice(getText("doclet.dest_dir_create",
                         destdirname));
-                    (new File(destdirname)).mkdirs();
+                    destDir.mkdirs();
                 } else if (!destDir.isDirectory()) {
                     reporter.printError(getText(
                         "doclet.destination_directory_not_directory_0",
@@ -711,7 +752,7 @@
     public InputStream getBuilderXML() throws FileNotFoundException {
         return builderXMLPath == null ?
             Configuration.class.getResourceAsStream(DEFAULT_BUILDER_XML) :
-            new FileInputStream(new File(builderXMLPath));
+            DocFile.createFileForInput(this, builderXMLPath).openInputStream();
     }
 
     /**
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AnnotationTypeBuilder.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AnnotationTypeBuilder.java	Wed Oct 31 13:48:15 2012 -0700
@@ -140,10 +140,7 @@
             //Only copy doc files dir if the containing package is not
             //documented AND if we have not documented a class from the same
             //package already. Otherwise, we are making duplicate copies.
-            Util.copyDocFiles(configuration,
-                new File(Util.getPackageSourcePath(configuration, containingPackage),
-                    DocPath.forPackage(annotationTypeDoc).getPath()),
-                DocPaths.DOC_FILES, true);
+            Util.copyDocFiles(configuration, containingPackage);
             containingPackagesSeen.add(containingPackage.name());
         }
      }
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/ClassBuilder.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/ClassBuilder.java	Wed Oct 31 13:48:15 2012 -0700
@@ -266,10 +266,7 @@
             //Only copy doc files dir if the containing package is not
             //documented AND if we have not documented a class from the same
             //package already. Otherwise, we are making duplicate copies.
-            Util.copyDocFiles(configuration,
-                new File(Util.getPackageSourcePath(configuration, containingPackage),
-                    DocPath.forPackage(classDoc).getPath()),
-                DocPaths.DOC_FILES, true);
+            Util.copyDocFiles(configuration, containingPackage);
             containingPackagesSeen.add(containingPackage.name());
         }
      }
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/PackageSummaryBuilder.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/PackageSummaryBuilder.java	Wed Oct 31 13:48:15 2012 -0700
@@ -119,11 +119,7 @@
         packageWriter.addPackageFooter(contentTree);
         packageWriter.printDocument(contentTree);
         packageWriter.close();
-        Util.copyDocFiles(
-                configuration,
-                Util.getPackageSourcePath(configuration, packageDoc),
-                DocPath.forPackage(packageDoc).resolve(DocPaths.DOC_FILES),
-                true);
+        Util.copyDocFiles(configuration, packageDoc);
     }
 
     /**
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/taglets/TagletManager.java	Wed Oct 31 13:48:15 2012 -0700
@@ -161,7 +161,7 @@
      * @param message the message retriever to print warnings.
      */
     public TagletManager(boolean nosince, boolean showversion,
-                         boolean showauthor, MessageRetriever message){
+                         boolean showauthor, MessageRetriever message) {
         overridenStandardTags = new HashSet<String>();
         potentiallyConflictingTags = new HashSet<String>();
         standardTags = new HashSet<String>();
@@ -253,47 +253,17 @@
      * @param path the search path string
      * @return the resulting array of directory and JAR file URLs
      */
-    private static URL[] pathToURLs(String path) {
-        StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
-        URL[] urls = new URL[st.countTokens()];
-        int count = 0;
-        while (st.hasMoreTokens()) {
-            URL url = fileToURL(new File(st.nextToken()));
-            if (url != null) {
-                urls[count++] = url;
+    private URL[] pathToURLs(String path) {
+        Set<URL> urls = new LinkedHashSet<URL>();
+        for (String s: path.split(File.pathSeparator)) {
+            if (s.isEmpty()) continue;
+            try {
+                urls.add(new File(s).getAbsoluteFile().toURI().toURL());
+            } catch (MalformedURLException e) {
+                message.error("doclet.MalformedURL", s);
             }
         }
-        urls = Arrays.copyOf(urls, count);
-        return urls;
-    }
-
-    /**
-     * Returns the directory or JAR file URL corresponding to the specified
-     * local file name.
-     *
-     * @param file the File object
-     * @return the resulting directory or JAR file URL, or null if unknown
-     */
-    private static URL fileToURL(File file) {
-        String name;
-        try {
-            name = file.getCanonicalPath();
-        } catch (IOException e) {
-            name = file.getAbsolutePath();
-        }
-        name = name.replace(File.separatorChar, '/');
-        if (!name.startsWith("/")) {
-            name = "/" + name;
-        }
-        // If the file does not exist, then assume that it's a directory
-        if (!file.isFile()) {
-            name = name + "/";
-        }
-        try {
-            return new URL("file", "", name);
-        } catch (MalformedURLException e) {
-            throw new IllegalArgumentException("file");
-        }
+        return urls.toArray(new URL[urls.size()]);
     }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFile.java	Wed Oct 31 13:48:15 2012 -0700
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.doclets.internal.toolkit.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.tools.JavaFileManager.Location;
+import javax.tools.StandardLocation;
+
+import com.sun.tools.doclets.internal.toolkit.Configuration;
+
+/**
+ * Abstraction for handling files, which may be specified directly
+ * (e.g. via a path on the command line) or relative to a Location.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ *
+ * @since 8
+ */
+public class DocFile {
+
+    /**
+     * The doclet configuration.
+     * Provides access to options such as docencoding, output directory, etc.
+     */
+    private final Configuration configuration;
+
+    /**
+     * The location for this file. Maybe null if the file was created without
+     * a location or path.
+     */
+    private final Location location;
+
+    /**
+     * The path relative to the (output) location. Maybe null if the file was
+     * created without a location or path.
+     */
+    private final DocPath path;
+
+    /**
+     * The file object itself.
+     * This is temporary, until we create different subtypes of DocFile.
+     */
+    private final File file;
+
+    /** Create a DocFile for a directory. */
+    public static DocFile createFileForDirectory(Configuration configuration, String file) {
+        return new DocFile(configuration, new File(file));
+    }
+
+    /** Create a DocFile for a file that will be opened for reading. */
+    public static DocFile createFileForInput(Configuration configuration, String file) {
+        return new DocFile(configuration, new File(file));
+    }
+
+    /** Create a DocFile for a file that will be opened for writing. */
+    public static DocFile createFileForOutput(Configuration configuration, DocPath path) {
+        return new DocFile(configuration, StandardLocation.CLASS_OUTPUT, path);
+    }
+
+    /**
+     * List the directories and files found in subdirectories along the
+     * elements of the given location.
+     * @param configuration the doclet configuration
+     * @param location currently, only {@link StandardLocation#SOURCE_PATH} is supported.
+     * @param path the subdirectory of the directories of the location for which to
+     *  list files
+     */
+    public static Iterable<DocFile> list(Configuration configuration, Location location, DocPath path) {
+        if (location != StandardLocation.SOURCE_PATH)
+            throw new IllegalArgumentException();
+
+        Set<DocFile> files = new LinkedHashSet<DocFile>();
+        for (String s : configuration.sourcepath.split(File.pathSeparator)) {
+            if (s.isEmpty())
+                continue;
+            File f = new File(s);
+            if (f.isDirectory()) {
+                f = new File(f, path.getPath());
+                if (f.exists())
+                    files.add(new DocFile(configuration, f));
+            }
+        }
+        return files;
+    }
+
+    /** Create a DocFile for a given file. */
+    private DocFile(Configuration configuration, File file) {
+        this.configuration = configuration;
+        this.location = null;
+        this.path = null;
+        this.file = file;
+    }
+
+    /** Create a DocFile for a given location and relative path. */
+    private DocFile(Configuration configuration, Location location, DocPath path) {
+        this.configuration = configuration;
+        this.location = location;
+        this.path = path;
+        this.file = path.resolveAgainst(configuration.destDirName);
+    }
+
+    /** Open an input stream for the file. */
+    public InputStream openInputStream() throws FileNotFoundException {
+        return new BufferedInputStream(new FileInputStream(file));
+    }
+
+    /**
+     * Open an output stream for the file.
+     * The file must have been created with a location of
+     * {@link StandardLocation#CLASS_OUTPUT} and a corresponding relative path.
+     */
+    public OutputStream openOutputStream() throws IOException, UnsupportedEncodingException {
+        if (location != StandardLocation.CLASS_OUTPUT)
+            throw new IllegalStateException();
+
+        createDirectoryForFile(file);
+        return new BufferedOutputStream(new FileOutputStream(file));
+    }
+
+    /**
+     * Open an writer for the file, using the encoding (if any) given in the
+     * doclet configuration.
+     * The file must have been created with a location of
+     * {@link StandardLocation#CLASS_OUTPUT} and a corresponding relative path.
+     */
+    public Writer openWriter() throws IOException, UnsupportedEncodingException {
+        if (location != StandardLocation.CLASS_OUTPUT)
+            throw new IllegalStateException();
+
+        createDirectoryForFile(file);
+        FileOutputStream fos = new FileOutputStream(file);
+        if (configuration.docencoding == null) {
+            return new BufferedWriter(new OutputStreamWriter(fos));
+        } else {
+            return new BufferedWriter(new OutputStreamWriter(fos, configuration.docencoding));
+        }
+    }
+
+    /**
+     * Copy the contents of another file directly to this file.
+     */
+    public void copyFile(DocFile fromFile) throws IOException {
+        if (location != StandardLocation.CLASS_OUTPUT)
+            throw new IllegalStateException();
+
+        createDirectoryForFile(file);
+
+        InputStream input = fromFile.openInputStream();
+        OutputStream output = openOutputStream();
+        try {
+            byte[] bytearr = new byte[1024];
+            int len;
+            while ((len = input.read(bytearr)) != -1) {
+                output.write(bytearr, 0, len);
+            }
+        } catch (FileNotFoundException exc) {
+        } catch (SecurityException exc) {
+        } finally {
+            input.close();
+            output.close();
+        }
+    }
+
+    /**
+     * Copy the contents of a resource file to this file.
+     * @param resource the path of the resource, relative to the package of this class
+     * @param overwrite whether or not to overwrite the file if it already exists
+     * @param replaceNewLine if false, the file is copied as a binary file;
+     *     if true, the file is written line by line, using the platform line
+     *     separator
+     */
+    public void copyResource(DocPath resource, boolean overwrite, boolean replaceNewLine) {
+        if (location != StandardLocation.CLASS_OUTPUT)
+            throw new IllegalStateException();
+
+        if (file.exists() && !overwrite)
+            return;
+
+        createDirectoryForFile(file);
+
+        try {
+            InputStream in = Configuration.class.getResourceAsStream(resource.getPath());
+            if (in == null)
+                return;
+
+            OutputStream out = new FileOutputStream(file);
+            try {
+                if (!replaceNewLine) {
+                    byte[] buf = new byte[2048];
+                    int n;
+                    while((n = in.read(buf))>0) out.write(buf,0,n);
+                } else {
+                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+                    BufferedWriter writer;
+                    if (configuration.docencoding == null) {
+                        writer = new BufferedWriter(new OutputStreamWriter(out));
+                    } else {
+                        writer = new BufferedWriter(new OutputStreamWriter(out,
+                                configuration.docencoding));
+                    }
+                    try {
+                        String line;
+                        while ((line = reader.readLine()) != null) {
+                            writer.write(line);
+                            writer.write(DocletConstants.NL);
+                        }
+                    } finally {
+                        reader.close();
+                        writer.close();
+                    }
+                }
+            } finally {
+                in.close();
+                out.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace(System.err);
+            throw new DocletAbortException();
+        }
+    }
+
+    /** Return true if the file can be read. */
+    public boolean canRead() {
+        return file.canRead();
+    }
+
+    /** Return true if the file can be written. */
+    public boolean canWrite() {
+        return file.canRead();
+    }
+
+    /** Return true if the file exists. */
+    public boolean exists() {
+        return file.exists();
+    }
+
+    /** Return the base name (last component) of the file name. */
+    public String getName() {
+        return file.getName();
+    }
+
+    /** Return the file system path for this file. */
+    public String getPath() {
+        return file.getPath();
+    }
+
+    /** Return true is file has an absolute path name. */
+    boolean isAbsolute() {
+        return file.isAbsolute();
+    }
+
+    /** Return true is file identifies a directory. */
+    public boolean isDirectory() {
+        return file.isDirectory();
+    }
+
+    /** Return true is file identifies a file. */
+    public boolean isFile() {
+        return file.isFile();
+    }
+
+    /** Return true if this file is the same as another. */
+    public boolean isSameFile(DocFile other) {
+        try {
+            return file.exists()
+                    && file.getCanonicalFile().equals(other.file.getCanonicalFile());
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /** If the file is a directory, list its contents. */
+    public Iterable<DocFile> list() {
+        List<DocFile> files = new ArrayList<DocFile>();
+        for (File f: file.listFiles()) {
+            files.add(new DocFile(configuration, f));
+        }
+        return files;
+    }
+
+    /** Create the file as a directory, including any parent directories. */
+    public boolean mkdirs() {
+        return file.mkdirs();
+    }
+
+    /**
+     * Derive a new file by resolving a relative path against this file.
+     * The new file will inherit the configuration and location of this file
+     * If this file has a path set, the new file will have a corresponding
+     * new path.
+     */
+    public DocFile resolve(DocPath p) {
+        return resolve(p.getPath());
+    }
+
+    /**
+     * Derive a new file by resolving a relative path against this file.
+     * The new file will inherit the configuration and location of this file
+     * If this file has a path set, the new file will have a corresponding
+     * new path.
+     */
+    public DocFile resolve(String p) {
+        if (location == null && path == null) {
+            return new DocFile(configuration, new File(file, p));
+        } else {
+            return new DocFile(configuration, location, path.resolve(p));
+        }
+    }
+
+    /**
+     * Resolve a relative file against the given output location.
+     * @param locn Currently, only SOURCE_OUTPUT is supported.
+     */
+    public DocFile resolveAgainst(StandardLocation locn) {
+        if (locn != StandardLocation.CLASS_OUTPUT)
+            throw new IllegalArgumentException();
+        return new DocFile(configuration,
+                new File(configuration.destDirName, file.getPath()));
+    }
+
+    /**
+     * Given a path string create all the directories in the path. For example,
+     * if the path string is "java/applet", the method will create directory
+     * "java" and then "java/applet" if they don't exist. The file separator
+     * string "/" is platform dependent system property.
+     *
+     * @param path Directory path string.
+     */
+    private void createDirectoryForFile(File file) {
+        File dir = file.getParentFile();
+        if (dir == null || dir.exists() || dir.mkdirs())
+            return;
+
+        configuration.message.error(
+               "doclet.Unable_to_create_directory_0", dir.getPath());
+        throw new DocletAbortException();
+    }
+
+    /** Return a string to identify the contents of this object,
+     * for debugging purposes.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DocFile[");
+        if (location != null)
+            sb.append("locn:").append(location).append(",");
+        if (path != null)
+            sb.append("path:").append(path.getPath()).append(",");
+        sb.append("file:").append(file);
+        sb.append("]");
+        return sb.toString();
+    }
+}
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocPaths.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocPaths.java	Wed Oct 31 13:48:15 2012 -0700
@@ -28,6 +28,11 @@
 /**
  * Standard DocPath objects.
  *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ *
  * @since 8
  */
 public class DocPaths {
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Extern.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Extern.java	Wed Oct 31 13:48:15 2012 -0700
@@ -30,6 +30,8 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.tools.StandardLocation;
+
 import com.sun.javadoc.*;
 import com.sun.tools.doclets.internal.toolkit.*;
 
@@ -177,7 +179,7 @@
             if (isUrl(pkglisturl)) {
                 readPackageListFromURL(url, toURL(pkglisturl));
             } else {
-                readPackageListFromFile(url, new File(pkglisturl));
+                readPackageListFromFile(url, DocFile.createFileForInput(configuration, pkglisturl));
             }
             return true;
         } catch (Fault f) {
@@ -247,16 +249,18 @@
      * @param path URL or directory path to the packages.
      * @param pkgListPath Path to the local "package-list" file.
      */
-    private void readPackageListFromFile(String path, File pkgListPath)
+    private void readPackageListFromFile(String path, DocFile pkgListPath)
             throws Fault {
-        File file = new File(pkgListPath, "package-list");
+        DocFile file = pkgListPath.resolve(DocPaths.PACKAGE_LIST);
         if (! (file.isAbsolute() || linkoffline)){
-            file = new File(configuration.destDirName, file.getPath());
+            file = file.resolveAgainst(StandardLocation.CLASS_OUTPUT);
         }
         try {
             if (file.exists() && file.canRead()) {
-                readPackageList(new FileInputStream(file), path,
-                    ! ((new File(path)).isAbsolute() || isUrl(path)));
+                boolean pathIsRelative =
+                        !DocFile.createFileForInput(configuration, path).isAbsolute()
+                        && !isUrl(path);
+                readPackageList(file.openInputStream(), path, pathIsRelative);
             } else {
                 throw new Fault(configuration.getText("doclet.File_error", file.getPath()), null);
             }
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/PackageListWriter.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/PackageListWriter.java	Wed Oct 31 13:48:15 2012 -0700
@@ -52,7 +52,7 @@
      * @param configuration the current configuration of the doclet.
      */
     public PackageListWriter(Configuration configuration) throws IOException {
-        super(Util.genWriter(configuration, DocPaths.PACKAGE_LIST));
+        super(DocFile.createFileForOutput(configuration, DocPaths.PACKAGE_LIST).openWriter());
         this.configuration = configuration;
     }
 
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/SourcePath.java	Wed Oct 31 08:31:40 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package com.sun.tools.doclets.internal.toolkit.util;
-
-import java.io.File;
-
-/**
- * This class is used to represent a source path which can contain only
- * directories no zip files. If a zip file is specified in the command line it
- * will not get reflected in the SourcePath.
- *
- * This code is not part of an API.
- * It is implementation that is subject to change.
- * Do not use it as an API
- *
- * @author Atul M Dambalkar
- */
-public class SourcePath {
-    private final char dirSeparator = File.pathSeparatorChar;
-
-    /**
-     * The original class path string
-     */
-    private String pathstr;
-
-    /**
-     * List of source path entries. Each entry is a directory.
-     */
-    private File[] sourcePath;
-
-
-    /**
-     * Build a source path from the specified path string on the command line.
-     */
-    public SourcePath(String pathstr) {
-        init(pathstr);
-    }
-
-    /**
-     * Build a default source path from the path strings specified by
-     * the properties env.class.path.
-     */
-    public SourcePath() {
-        init(System.getProperty("env.class.path"));
-    }
-
-    /**
-     * Initialize the SourcePath File array, which will contain only the
-     * directory names from the given path string.
-     *
-     * @param pathstr Path String.
-     */
-    private void init(String pathstr) {
-        if (pathstr == null ||  pathstr.length() == 0) {
-            pathstr = ".";
-        }
-
-        int noOfFileSep = 0;
-        int index = 0;
-        this.pathstr = pathstr; // Save original class path string
-
-        // Count the number of path separators
-        while ((index = pathstr.indexOf(dirSeparator, index)) != -1) {
-            noOfFileSep++;
-            index++;
-        }
-        // Build the source path
-        File[] tempPath = new File[noOfFileSep + 1];
-        int tempPathIndex = 0;
-        int len = pathstr.length();
-        int sepPos = 0;
-        for (index = 0; index < len; index = sepPos + 1) {
-            sepPos = pathstr.indexOf(dirSeparator, index);
-            if (sepPos < 0) {
-                sepPos = len;
-            }
-            File file = new File(pathstr.substring(index, sepPos));
-            if (file.isDirectory()) {
-                tempPath[tempPathIndex++] = file;
-            } // if it is really a file, ignore it.
-        }
-        sourcePath = new File[tempPathIndex];
-        System.arraycopy((Object)tempPath, 0, (Object)sourcePath,
-                         0, tempPathIndex);
-    }
-
-    /**
-     * Find the specified directory in the source path.
-     *
-     * @param p Name of the directory to be searched for in the source path.
-     * @return File Return the directory if found else return null.
-     */
-    public File getDirectory(DocPath p) {
-        for (int i = 0; i < sourcePath.length; i++) {
-            File directoryNeeded = new File(sourcePath[i], p.getPath());
-            if (directoryNeeded.isDirectory()) {
-                return directoryNeeded;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Return original source path string.
-     */
-    public String toString() {
-        return pathstr;
-    }
-}
--- a/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Util.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/Util.java	Wed Oct 31 13:48:15 2012 -0700
@@ -30,6 +30,7 @@
 
 import com.sun.javadoc.*;
 import com.sun.tools.doclets.internal.toolkit.*;
+import javax.tools.StandardLocation;
 
 /**
  * Utilities Class for Doclets.
@@ -192,32 +193,6 @@
     }
 
     /**
-     * Copy source file to destination file.
-     *
-     * @throws SecurityException
-     * @throws IOException
-     */
-    public static void copyFile(File destfile, File srcfile)
-        throws IOException {
-        byte[] bytearr = new byte[512];
-        int len = 0;
-        FileInputStream input = new FileInputStream(srcfile);
-        File destDir = destfile.getParentFile();
-        destDir.mkdirs();
-        FileOutputStream output = new FileOutputStream(destfile);
-        try {
-            while ((len = input.read(bytearr)) != -1) {
-                output.write(bytearr, 0, len);
-                }
-        } catch (FileNotFoundException exc) {
-        } catch (SecurityException exc) {
-            } finally {
-            input.close();
-            output.close();
-            }
-        }
-
-    /**
      * Copy the given directory contents from the source package directory
      * to the generated documentation directory. For example for a package
      * java.lang this method find out the source location of the package using
@@ -230,38 +205,45 @@
      * @param dir The original directory name to copy from.
      * @param overwrite Overwrite files if true.
      */
-    public static void copyDocFiles(Configuration configuration,
-            File path, DocPath dir, boolean overwrite) {
-        if (checkCopyDocFilesErrors(configuration, path, dir)) {
-            return;
-        }
-        File srcdir = new File(path, dir.getPath());
-        File destdir = new File(configuration.docFileDestDirName, dir.getPath());
+    public static void copyDocFiles(Configuration configuration, PackageDoc pd) {
+        copyDocFiles(configuration, DocPath.forPackage(pd).resolve(DocPaths.DOC_FILES));
+    }
+
+    public static void copyDocFiles(Configuration configuration, DocPath dir) {
         try {
-            createDirectory(configuration, destdir);
-            String[] files = srcdir.list();
-            for (int i = 0; i < files.length; i++) {
-                File srcfile = new File(srcdir, files[i]);
-                File destfile = new File(destdir, files[i]);
-                if (srcfile.isFile()) {
-                    if (destfile.exists() && ! overwrite) {
-                        configuration.message.warning((SourcePosition) null,
-                                "doclet.Copy_Overwrite_warning",
-                                srcfile.toString(), destdir.toString());
-                    } else {
-                        configuration.message.notice(
-                            "doclet.Copying_File_0_To_Dir_1",
-                            srcfile.toString(), destdir.toString());
-                        Util.copyFile(destfile, srcfile);
-                    }
-                } else if (srcfile.isDirectory()) {
-                    if (configuration.copydocfilesubdirs
-                        && ! configuration.shouldExcludeDocFileDir(
-                          srcfile.getName())){
-                        copyDocFiles(configuration, path, dir.resolve(files[i]),
-                                overwrite);
+            boolean first = true;
+            for (DocFile f : DocFile.list(configuration, StandardLocation.SOURCE_PATH, dir)) {
+                if (!f.isDirectory()) {
+                    continue;
+                }
+                DocFile srcdir = f;
+                DocFile destdir = DocFile.createFileForOutput(configuration, dir);
+                if (srcdir.isSameFile(destdir)) {
+                    continue;
+                }
+
+                for (DocFile srcfile: srcdir.list()) {
+                    DocFile destfile = destdir.resolve(srcfile.getName());
+                    if (srcfile.isFile()) {
+                        if (destfile.exists() && !first) {
+                            configuration.message.warning((SourcePosition) null,
+                                    "doclet.Copy_Overwrite_warning",
+                                    srcfile.getPath(), destdir.getPath());
+                        } else {
+                            configuration.message.notice(
+                                    "doclet.Copying_File_0_To_Dir_1",
+                                    srcfile.getPath(), destdir.getPath());
+                            destfile.copyFile(srcfile);
+                        }
+                    } else if (srcfile.isDirectory()) {
+                        if (configuration.copydocfilesubdirs
+                                && !configuration.shouldExcludeDocFileDir(srcfile.getName())) {
+                            copyDocFiles(configuration, dir.resolve(srcfile.getName()));
+                        }
                     }
                 }
+
+                first = false;
             }
         } catch (SecurityException exc) {
             throw new DocletAbortException();
@@ -271,170 +253,6 @@
     }
 
     /**
-     * Given the parameters for copying doc-files, check for errors.
-     *
-     * @param configuration The configuration of the current doclet.
-     * @param path The relative path to the directory to be copied.
-     * @param dirName The original directory name to copy from.
-     */
-    private static boolean checkCopyDocFilesErrors (Configuration configuration,
-            File path, DocPath dirName) {
-        if ((configuration.sourcepath == null || configuration.sourcepath.length() == 0) &&
-               (configuration.destDirName == null || configuration.destDirName.length() == 0)) {
-            //The destination path and source path are definitely equal.
-            return true;
-        }
-        File sourcePath, destPath = new File(configuration.destDirName);
-        StringTokenizer pathTokens = new StringTokenizer(
-            configuration.sourcepath == null ? "" : configuration.sourcepath,
-            File.pathSeparator);
-        //Check if the destination path is equal to the source path.  If yes,
-        //do not copy doc-file directories.
-        while(pathTokens.hasMoreTokens()){
-            sourcePath = new File(pathTokens.nextToken());
-            if(destPath.equals(sourcePath)){
-                return true;
-            }
-        }
-        //Make sure the doc-file being copied exists.
-        File srcdir = new File(path, dirName.getPath());
-        if (! srcdir.exists()) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Copy a file in the resources directory to the destination
-     * directory (if it is not there already).  If
-     * <code>overwrite</code> is true and the destination file
-     * already exists, overwrite it.
-     *
-     * @param configuration  Holds the destination directory and error message
-     * @param resourcefile   The name of the resource file to copy
-     * @param overwrite      A flag to indicate whether the file in the
-     *                       destination directory will be overwritten if
-     *                       it already exists.
-     */
-    public static void copyResourceFile(Configuration configuration,
-            String resourcefile, boolean overwrite) {
-        copyFile(configuration, resourcefile, DocPaths.RESOURCES, DocPaths.RESOURCES,
-                overwrite, false);
-    }
-
-    /**
-     * Copy a file from a source directory to a destination directory
-     * (if it is not there already). If <code>overwrite</code> is true and
-     * the destination file already exists, overwrite it.
-     *
-     * @param configuration Holds the error message
-     * @param file The name of the file to copy
-     * @param source The source directory
-     * @param destination The destination directory where the file needs to be copied
-     * @param overwrite A flag to indicate whether the file in the
-     *                  destination directory will be overwritten if
-     *                  it already exists.
-     * @param replaceNewLine true if the newline needs to be replaced with platform-
-     *                  specific newline.
-     */
-    public static void copyFile(Configuration configuration, String file, DocPath source,
-            DocPath destination, boolean overwrite, boolean replaceNewLine) {
-        copyFile(configuration, file, source.getPath(), destination.getPath(),
-                overwrite, replaceNewLine);
-    }
-
-    public static void copyFile(Configuration configuration, String file, String source,
-            String destination, boolean overwrite, boolean replaceNewLine) {
-        File destdir = configuration.destDirName.isEmpty() ?
-                (destination.isEmpty() ? null : new File(destination)) :
-                new File(configuration.destDirName, destination);
-        File destfile = (destdir == null) ? new File(file) : new File(destdir, file);
-        createDirectory(configuration, destfile.getParentFile());
-        if (destfile.exists() && (! overwrite)) return;
-        try {
-            InputStream in = Configuration.class.getResourceAsStream(
-                    source + '/' + file);
-            if (in == null) return;
-            OutputStream out = new FileOutputStream(destfile);
-            try {
-                if (!replaceNewLine) {
-                    byte[] buf = new byte[2048];
-                    int n;
-                    while((n = in.read(buf))>0) out.write(buf,0,n);
-                } else {
-                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
-                    BufferedWriter writer;
-                    if (configuration.docencoding == null) {
-                        writer = new BufferedWriter(new OutputStreamWriter(out));
-                    } else {
-                        writer = new BufferedWriter(new OutputStreamWriter(out,
-                            configuration.docencoding));
-                    }
-                    try {
-                        String line;
-                        while ((line = reader.readLine()) != null) {
-                            writer.write(line);
-                            writer.write(DocletConstants.NL);
-                        }
-                    } finally {
-                        reader.close();
-                        writer.close();
-                    }
-                }
-            } finally {
-                in.close();
-                out.close();
-            }
-        } catch (IOException ie) {
-            ie.printStackTrace(System.err);
-            throw new DocletAbortException();
-        }
-    }
-
-    /**
-     * Given a path string create all the directories in the path. For example,
-     * if the path string is "java/applet", the method will create directory
-     * "java" and then "java/applet" if they don't exist. The file separator
-     * string "/" is platform dependent system property.
-     *
-     * @param dir Directory path string.
-     */
-    public static void createDirectory(Configuration configuration, File dir) {
-        if (dir == null) {
-            return;
-        }
-        if (dir.exists()) {
-            return;
-        } else {
-            if (dir.mkdirs()) {
-                return;
-            } else {
-                configuration.message.error(
-                       "doclet.Unable_to_create_directory_0", dir.getPath());
-                throw new DocletAbortException();
-            }
-        }
-    }
-
-    /**
-     * Given a PackageDoc, return the source path for that package.
-     * @param configuration The Configuration for the current Doclet.
-     * @param pkgDoc The package to search the path for.
-     * @return A string representing the path to the given package.
-     */
-    public static File getPackageSourcePath(Configuration configuration,
-            PackageDoc pkgDoc) {
-        DocPath pkgPath = DocPath.forPackage(pkgDoc);
-        File pkgDir = new SourcePath(configuration.sourcepath).getDirectory(pkgPath);
-        if (pkgDir == null)
-            return null;
-        //Make sure that both paths are using the same separators.
-        String completePath = Util.replaceText(pkgDir.getPath(), File.separator, "/");
-        String pathForPkg = completePath.substring(0, completePath.lastIndexOf(pkgPath.getPath()));
-        return new File(pathForPkg);
-    }
-
-    /**
      * We want the list of types in alphabetical order.  However, types are not
      * comparable.  We need a comparator for now.
      */
@@ -639,31 +457,6 @@
     }
 
     /**
-     * Create the directory path for the file to be generated, construct
-     * FileOutputStream and OutputStreamWriter depending upon docencoding.
-     *
-     * @param path The directory path to be created for this file.
-     * @exception IOException Exception raised by the FileWriter is passed on
-     * to next level.
-     * @exception UnsupportedEncodingException Exception raised by the
-     * OutputStreamWriter is passed on to next level.
-     * @return Writer Writer for the file getting generated.
-     * @see java.io.FileOutputStream
-     * @see java.io.OutputStreamWriter
-     */
-    public static Writer genWriter(Configuration configuration, DocPath path)
-            throws IOException, UnsupportedEncodingException {
-        File file = path.resolveAgainst(configuration.destDirName);
-        createDirectory(configuration, file.getParentFile());
-        FileOutputStream fos = new FileOutputStream(file);
-        if (configuration.docencoding == null) {
-            return new BufferedWriter(new OutputStreamWriter(fos));
-        } else {
-            return new BufferedWriter(new OutputStreamWriter(fos, configuration.docencoding));
-        }
-    }
-
-    /**
      * Given an annotation, return true if it should be documented and false
      * otherwise.
      *
@@ -683,47 +476,6 @@
     }
 
     /**
-     * Given a string, return an array of tokens.  The separator can be escaped
-     * with the '\' character.  The '\' character may also be escaped by the
-     * '\' character.
-     *
-     * @param s         the string to tokenize.
-     * @param separator the separator char.
-     * @param maxTokens the maxmimum number of tokens returned.  If the
-     *                  max is reached, the remaining part of s is appended
-     *                  to the end of the last token.
-     *
-     * @return an array of tokens.
-     */
-    public static String[] tokenize(String s, char separator, int maxTokens) {
-        List<String> tokens = new ArrayList<String>();
-        StringBuilder  token = new StringBuilder ();
-        boolean prevIsEscapeChar = false;
-        for (int i = 0; i < s.length(); i += Character.charCount(i)) {
-            int currentChar = s.codePointAt(i);
-            if (prevIsEscapeChar) {
-                // Case 1:  escaped character
-                token.appendCodePoint(currentChar);
-                prevIsEscapeChar = false;
-            } else if (currentChar == separator && tokens.size() < maxTokens-1) {
-                // Case 2:  separator
-                tokens.add(token.toString());
-                token = new StringBuilder();
-            } else if (currentChar == '\\') {
-                // Case 3:  escape character
-                prevIsEscapeChar = true;
-            } else {
-                // Case 4:  regular character
-                token.appendCodePoint(currentChar);
-            }
-        }
-        if (token.length() > 0) {
-            tokens.add(token.toString());
-        }
-        return tokens.toArray(new String[] {});
-    }
-
-    /**
      * Return true if this class is linkable and false if we can't link to the
      * desired class.
      * <br>
--- a/langtools/test/com/sun/javadoc/testDocFileDir/TestDocFileDir.java	Wed Oct 31 08:31:40 2012 -0700
+++ b/langtools/test/com/sun/javadoc/testDocFileDir/TestDocFileDir.java	Wed Oct 31 13:48:15 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -77,17 +77,19 @@
     //Output dir = Input Dir
     private static final String[] ARGS1 =
         new String[] {
-            "-d", BUG_ID + "-1", "-sourcepath",
-            "blah" + String.valueOf(File.pathSeparatorChar) +
-                BUG_ID + "-1" + String.valueOf(File.pathSeparatorChar) +
-                "blah", "pkg"};
+            "-d", BUG_ID + "-1",
+            "-sourcepath",
+                "blah" + File.pathSeparator + BUG_ID + "-1" + File.pathSeparator + "blah",
+            "pkg"};
 
     //Exercising -docfilessubdirs and -excludedocfilessubdir
     private static final String[] ARGS2 =
         new String[] {
-            "-d", BUG_ID + "-2", "-sourcepath", SRC_DIR,
-            "-docfilessubdirs", "-excludedocfilessubdir",
-            "subdir-excluded1:subdir-excluded2", "pkg"};
+            "-d", BUG_ID + "-2",
+            "-sourcepath", SRC_DIR,
+            "-docfilessubdirs",
+            "-excludedocfilessubdir", "subdir-excluded1:subdir-excluded2",
+            "pkg"};
 
     //Output dir = "", Input dir = ""
     private static final String[] ARGS0 =