1 /*
   2  * Copyright (c) 2005, 2011, 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 com.sun.tools.javac.file;
  27 
  28 import java.io.File;
  29 import java.io.FileInputStream;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.OutputStream;
  34 import java.io.OutputStreamWriter;
  35 import java.io.Writer;
  36 import java.lang.ref.Reference;
  37 import java.lang.ref.SoftReference;
  38 import java.net.URI;
  39 import java.nio.ByteBuffer;
  40 import java.nio.CharBuffer;
  41 import java.nio.charset.CharsetDecoder;
  42 import javax.tools.JavaFileObject;
  43 import java.text.Normalizer;
  44 
  45 /**
  46  * A subclass of JavaFileObject representing regular files.
  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 class RegularFileObject extends BaseFileObject {
  54 
  55     /** Have the parent directories been created?
  56      */
  57     private boolean hasParents = false;
  58     private String name;
  59     final File file;
  60     private Reference<File> absFileRef;
  61     final static boolean isMacOS = System.getProperty("os.name", "").contains("OS X");
  62 
  63     public RegularFileObject(JavacFileManager fileManager, File f) {
  64         this(fileManager, f.getName(), f);
  65     }
  66 
  67     public RegularFileObject(JavacFileManager fileManager, String name, File f) {
  68         super(fileManager);
  69         if (f.isDirectory()) {
  70             throw new IllegalArgumentException("directories not supported");
  71         }
  72         this.name = name;
  73         this.file = f;
  74     }
  75 
  76     @Override
  77     public URI toUri() {
  78         return file.toURI().normalize();
  79     }
  80 
  81     @Override
  82     public String getName() {
  83         return file.getPath();
  84     }
  85 
  86     @Override
  87     public String getShortName() {
  88         return name;
  89     }
  90 
  91     @Override
  92     public JavaFileObject.Kind getKind() {
  93         return getKind(name);
  94     }
  95 
  96     @Override
  97     public InputStream openInputStream() throws IOException {
  98         return new FileInputStream(file);
  99     }
 100 
 101     @Override
 102     public OutputStream openOutputStream() throws IOException {
 103         fileManager.flushCache(this);
 104         ensureParentDirectoriesExist();
 105         return new FileOutputStream(file);
 106     }
 107 
 108     @Override
 109     public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
 110         CharBuffer cb = fileManager.getCachedContent(this);
 111         if (cb == null) {
 112             InputStream in = new FileInputStream(file);
 113             try {
 114                 ByteBuffer bb = fileManager.makeByteBuffer(in);
 115                 JavaFileObject prev = fileManager.log.useSource(this);
 116                 try {
 117                     cb = fileManager.decode(bb, ignoreEncodingErrors);
 118                 } finally {
 119                     fileManager.log.useSource(prev);
 120                 }
 121                 fileManager.recycleByteBuffer(bb);
 122                 if (!ignoreEncodingErrors) {
 123                     fileManager.cache(this, cb);
 124                 }
 125             } finally {
 126                 in.close();
 127             }
 128         }
 129         return cb;
 130     }
 131 
 132     @Override
 133     public Writer openWriter() throws IOException {
 134         fileManager.flushCache(this);
 135         ensureParentDirectoriesExist();
 136         return new OutputStreamWriter(new FileOutputStream(file), fileManager.getEncodingName());
 137     }
 138 
 139     @Override
 140     public long getLastModified() {
 141         return file.lastModified();
 142     }
 143 
 144     @Override
 145     public boolean delete() {
 146         return file.delete();
 147     }
 148 
 149     @Override
 150     protected CharsetDecoder getDecoder(boolean ignoreEncodingErrors) {
 151         return fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors);
 152     }
 153 
 154     @Override
 155     protected String inferBinaryName(Iterable<? extends File> path) {
 156         String fPath = file.getPath();
 157         //System.err.println("RegularFileObject " + file + " " +r.getPath());
 158         for (File dir: path) {
 159             //System.err.println("dir: " + dir);
 160             String dPath = dir.getPath();
 161             if (dPath.length() == 0)
 162                 dPath = System.getProperty("user.dir");
 163             if (!dPath.endsWith(File.separator))
 164                 dPath += File.separator;
 165             if (fPath.regionMatches(true, 0, dPath, 0, dPath.length())
 166                 && new File(fPath.substring(0, dPath.length())).equals(new File(dPath))) {
 167                 String relativeName = fPath.substring(dPath.length());
 168                 return removeExtension(relativeName).replace(File.separatorChar, '.');
 169             }
 170         }
 171         return null;
 172     }
 173 
 174     @Override
 175     public boolean isNameCompatible(String cn, JavaFileObject.Kind kind) {
 176         cn.getClass();
 177         // null check
 178         if (kind == Kind.OTHER && getKind() != kind) {
 179             return false;
 180         }
 181         String n = cn + kind.extension;
 182         if (name.equals(n)) {
 183             return true;
 184         }
 185         if (isMacOS && Normalizer.isNormalized(name, Normalizer.Form.NFD)
 186             && Normalizer.isNormalized(n, Normalizer.Form.NFC)) {
 187             // On Mac OS X it is quite possible to file name and class
 188             // name normalized in a different way - in that case we have to normalize file name
 189             // to the Normal Form Compised (NFC)
 190             String normName = Normalizer.normalize(name, Normalizer.Form.NFC);
 191             if (normName.equals(n)) {
 192                 this.name = normName;
 193                 return true;
 194             }
 195         }
 196 
 197             if (name.equalsIgnoreCase(n)) {
 198             try {
 199                 // allow for Windows
 200                 return file.getCanonicalFile().getName().equals(n);
 201             } catch (IOException e) {
 202             }
 203         }
 204         return false;
 205     }
 206 
 207     private void ensureParentDirectoriesExist() throws IOException {
 208         if (!hasParents) {
 209             File parent = file.getParentFile();
 210             if (parent != null && !parent.exists()) {
 211                 if (!parent.mkdirs()) {
 212                     if (!parent.exists() || !parent.isDirectory()) {
 213                         throw new IOException("could not create parent directories");
 214                     }
 215                 }
 216             }
 217             hasParents = true;
 218         }
 219     }
 220 
 221     /**
 222      * Check if two file objects are equal.
 223      * Two RegularFileObjects are equal if the absolute paths of the underlying
 224      * files are equal.
 225      */
 226     @Override
 227     public boolean equals(Object other) {
 228         if (this == other)
 229             return true;
 230 
 231         if (!(other instanceof RegularFileObject))
 232             return false;
 233 
 234         RegularFileObject o = (RegularFileObject) other;
 235         return getAbsoluteFile().equals(o.getAbsoluteFile());
 236     }
 237 
 238     @Override
 239     public int hashCode() {
 240         return getAbsoluteFile().hashCode();
 241     }
 242 
 243     private File getAbsoluteFile() {
 244         File absFile = (absFileRef == null ? null : absFileRef.get());
 245         if (absFile == null) {
 246             absFile = file.getAbsoluteFile();
 247             absFileRef = new SoftReference<File>(absFile);
 248         }
 249         return absFile;
 250     }
 251 }