1 /*
   2  * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.javadoc.internal.doclets.toolkit.util;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.BufferedWriter;
  30 import java.io.FileNotFoundException;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.OutputStream;
  35 import java.io.OutputStreamWriter;
  36 import java.io.UnsupportedEncodingException;
  37 import java.io.Writer;
  38 
  39 import javax.tools.JavaFileManager.Location;
  40 import javax.tools.StandardLocation;
  41 
  42 import jdk.javadoc.internal.doclets.toolkit.Configuration;
  43 
  44 /**
  45  * Abstraction for handling files, which may be specified directly
  46  * (e.g. via a path on the command line) or relative to a Location.
  47  *
  48  *  <p><b>This is NOT part of any supported API.
  49  *  If you write code that depends on this, you do so at your own risk.
  50  *  This code and its internal interfaces are subject to change or
  51  *  deletion without notice.</b>
  52  *
  53  * @since 8
  54  */
  55 public abstract class DocFile {
  56 
  57     /** Create a DocFile for a directory. */
  58     public static DocFile createFileForDirectory(Configuration configuration, String file) {
  59         return DocFileFactory.getFactory(configuration).createFileForDirectory(file);
  60     }
  61 
  62     /** Create a DocFile for a file that will be opened for reading. */
  63     public static DocFile createFileForInput(Configuration configuration, String file) {
  64         return DocFileFactory.getFactory(configuration).createFileForInput(file);
  65     }
  66 
  67     /** Create a DocFile for a file that will be opened for writing. */
  68     public static DocFile createFileForOutput(Configuration configuration, DocPath path) {
  69         return DocFileFactory.getFactory(configuration).createFileForOutput(path);
  70     }
  71 
  72     private final Configuration configuration;
  73 
  74     /**
  75      * The location for this file. Maybe null if the file was created without
  76      * a location or path.
  77      */
  78     protected final Location location;
  79 
  80     /**
  81      * The path relative to the (output) location. Maybe null if the file was
  82      * created without a location or path.
  83      */
  84     protected final DocPath path;
  85 
  86     /**
  87      * List the directories and files found in subdirectories along the
  88      * elements of the given location.
  89      * @param configuration the doclet configuration
  90      * @param location currently, only {@link StandardLocation#SOURCE_PATH} is supported.
  91      * @param path the subdirectory of the directories of the location for which to
  92      *  list files
  93      */
  94     public static Iterable<DocFile> list(Configuration configuration, Location location, DocPath path) {
  95         return DocFileFactory.getFactory(configuration).list(location, path);
  96     }
  97 
  98     /** Create a DocFile without a location or path */
  99     protected DocFile(Configuration configuration) {
 100         this.configuration = configuration;
 101         this.location = null;
 102         this.path = null;
 103     }
 104 
 105     /** Create a DocFile for a given location and relative path. */
 106     protected DocFile(Configuration configuration, Location location, DocPath path) {
 107         this.configuration = configuration;
 108         this.location = location;
 109         this.path = path;
 110     }
 111 
 112     /** Open an input stream for the file. */
 113     public abstract InputStream openInputStream() throws IOException;
 114 
 115     /**
 116      * Open an output stream for the file.
 117      * The file must have been created with a location of
 118      * {@link DocumentationTool.Location#DOCUMENTATION_OUTPUT}
 119      * and a corresponding relative path.
 120      */
 121     public abstract OutputStream openOutputStream() throws IOException, UnsupportedEncodingException;
 122 
 123     /**
 124      * Open an writer for the file, using the encoding (if any) given in the
 125      * doclet configuration.
 126      * The file must have been created with a location of
 127      * {@link DocumentationTool.Location#DOCUMENTATION_OUTPUT} and a corresponding relative path.
 128      */
 129     public abstract Writer openWriter() throws IOException, UnsupportedEncodingException;
 130 
 131     /**
 132      * Copy the contents of another file directly to this file.
 133      */
 134     public void copyFile(DocFile fromFile) throws IOException {
 135         try (OutputStream output = openOutputStream();
 136              InputStream input = fromFile.openInputStream()) {
 137             byte[] bytearr = new byte[1024];
 138             int len;
 139             while ((len = input.read(bytearr)) != -1) {
 140                 output.write(bytearr, 0, len);
 141             }
 142         }
 143         catch (FileNotFoundException | SecurityException exc) {
 144         }
 145     }
 146 
 147     /**
 148      * Copy the contents of a resource file to this file.
 149      * @param resource the path of the resource, relative to the package of this class
 150      * @param overwrite whether or not to overwrite the file if it already exists
 151      * @param replaceNewLine if false, the file is copied as a binary file;
 152      *     if true, the file is written line by line, using the platform line
 153      *     separator
 154      */
 155     public void copyResource(DocPath resource, boolean overwrite, boolean replaceNewLine) {
 156         if (exists() && !overwrite)
 157             return;
 158 
 159         try {
 160             InputStream in = Configuration.class.getResourceAsStream(resource.getPath());
 161             if (in == null)
 162                 return;
 163 
 164             try (OutputStream out = openOutputStream()) {
 165                 if (!replaceNewLine) {
 166                     byte[] buf = new byte[2048];
 167                     int n;
 168                     while ((n = in.read(buf)) > 0)
 169                         out.write(buf, 0, n);
 170                 } else {
 171                     try (BufferedReader reader = new BufferedReader(new InputStreamReader(in));
 172                          BufferedWriter writer = new BufferedWriter(configuration.docencoding == null
 173                                                                     ? new OutputStreamWriter(out)
 174                                                                     : new OutputStreamWriter(out, configuration.docencoding))) {
 175                         String line;
 176                         while ((line = reader.readLine()) != null) {
 177                             writer.write(line);
 178                             writer.write(DocletConstants.NL);
 179                         }
 180                     }
 181                 }
 182             } finally {
 183                 in.close();
 184             }
 185         } catch (IOException e) {
 186             e.printStackTrace(System.err);
 187             throw new DocletAbortException(e);
 188         }
 189     }
 190 
 191     /** Return true if the file can be read. */
 192     public abstract boolean canRead();
 193 
 194     /** Return true if the file can be written. */
 195     public abstract boolean canWrite();
 196 
 197     /** Return true if the file exists. */
 198     public abstract boolean exists();
 199 
 200     /** Return the base name (last component) of the file name. */
 201     public abstract String getName();
 202 
 203     /** Return the file system path for this file. */
 204     public abstract String getPath();
 205 
 206     /** Return true if file has an absolute path name. */
 207     public abstract boolean isAbsolute();
 208 
 209     /** Return true if file identifies a directory. */
 210     public abstract boolean isDirectory();
 211 
 212     /** Return true if file identifies a file. */
 213     public abstract boolean isFile();
 214 
 215     /** Return true if this file is the same as another. */
 216     public abstract boolean isSameFile(DocFile other);
 217 
 218     /** If the file is a directory, list its contents. */
 219     public abstract Iterable<DocFile> list() throws IOException;
 220 
 221     /** Create the file as a directory, including any parent directories. */
 222     public abstract boolean mkdirs();
 223 
 224     /**
 225      * Derive a new file by resolving a relative path against this file.
 226      * The new file will inherit the configuration and location of this file
 227      * If this file has a path set, the new file will have a corresponding
 228      * new path.
 229      */
 230     public abstract DocFile resolve(DocPath p);
 231 
 232     /**
 233      * Derive a new file by resolving a relative path against this file.
 234      * The new file will inherit the configuration and location of this file
 235      * If this file has a path set, the new file will have a corresponding
 236      * new path.
 237      */
 238     public abstract DocFile resolve(String p);
 239 
 240     /**
 241      * Resolve a relative file against the given output location.
 242      * @param locn Currently, only
 243      * {@link DocumentationTool.Location#DOCUMENTATION_OUTPUT} is supported.
 244      */
 245     public abstract DocFile resolveAgainst(Location locn);
 246 }