jjg@450: /* ohair@962: * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. jjg@450: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@450: * jjg@450: * This code is free software; you can redistribute it and/or modify it jjg@450: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this jjg@450: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. jjg@450: * jjg@450: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@450: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@450: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@450: * version 2 for more details (a copy is included in the LICENSE file that jjg@450: * accompanied this code). jjg@450: * jjg@450: * You should have received a copy of the GNU General Public License version jjg@450: * 2 along with this work; if not, write to the Free Software Foundation, jjg@450: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@450: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. jjg@450: */ jjg@450: jjg@450: package com.sun.tools.javac.nio; jjg@450: jjg@450: import java.io.IOException; jjg@450: import java.io.InputStream; jjg@450: import java.io.InputStreamReader; jjg@450: import java.io.OutputStream; jjg@450: import java.io.OutputStreamWriter; jjg@450: import java.io.Reader; jjg@450: import java.io.Writer; jjg@450: import java.net.URI; jjg@450: import java.nio.ByteBuffer; jjg@450: import java.nio.CharBuffer; jjg@450: import java.nio.charset.CharsetDecoder; jjg@450: import java.nio.file.Files; jjg@450: import java.nio.file.Path; jjg@450: import java.nio.file.attribute.BasicFileAttributes; jjg@450: import javax.lang.model.element.Modifier; jjg@450: import javax.lang.model.element.NestingKind; jjg@450: import javax.tools.JavaFileObject; jjg@450: jjg@450: import com.sun.tools.javac.util.BaseFileManager; jjg@450: jjg@450: jjg@450: /** jjg@450: * Implementation of JavaFileObject using java.nio.file API. jjg@450: * jjg@450: *

PathFileObjects are, for the most part, straightforward wrappers around jjg@450: * Path objects. The primary complexity is the support for "inferBinaryName". jjg@450: * This is left as an abstract method, implemented by each of a number of jjg@450: * different factory methods, which compute the binary name based on jjg@450: * information available at the time the file object is created. jjg@450: * jjg@581: *

This is NOT part of any supported API. jjg@581: * If you write code that depends on this, you do so at your own risk. jjg@450: * This code and its internal interfaces are subject to change or jjg@450: * deletion without notice. jjg@450: */ jjg@450: abstract class PathFileObject implements JavaFileObject { jjg@450: private JavacPathFileManager fileManager; jjg@450: private Path path; jjg@450: jjg@450: /** jjg@450: * Create a PathFileObject within a directory, such that the binary name jjg@450: * can be inferred from the relationship to the parent directory. jjg@450: */ jjg@450: static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager, jjg@450: final Path path, final Path dir) { jjg@450: return new PathFileObject(fileManager, path) { jjg@450: @Override jjg@450: String inferBinaryName(Iterable paths) { jjg@450: return toBinaryName(dir.relativize(path)); jjg@450: } jjg@450: }; jjg@450: } jjg@450: jjg@450: /** jjg@450: * Create a PathFileObject in a file system such as a jar file, such that jjg@450: * the binary name can be inferred from its position within the filesystem. jjg@450: */ jjg@450: static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager, jjg@450: final Path path) { jjg@450: return new PathFileObject(fileManager, path) { jjg@450: @Override jjg@450: String inferBinaryName(Iterable paths) { jjg@450: return toBinaryName(path); jjg@450: } jjg@450: }; jjg@450: } jjg@450: jjg@450: /** jjg@450: * Create a PathFileObject whose binary name can be inferred from the jjg@450: * relative path to a sibling. jjg@450: */ jjg@450: static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager, jjg@450: final Path path, final String relativePath) { jjg@450: return new PathFileObject(fileManager, path) { jjg@450: @Override jjg@450: String inferBinaryName(Iterable paths) { jjg@450: return toBinaryName(relativePath, "/"); jjg@450: } jjg@450: }; jjg@450: } jjg@450: jjg@450: /** jjg@450: * Create a PathFileObject whose binary name might be inferred from its jjg@450: * position on a search path. jjg@450: */ jjg@450: static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager, jjg@450: final Path path) { jjg@450: return new PathFileObject(fileManager, path) { jjg@450: @Override jjg@450: String inferBinaryName(Iterable paths) { jjg@450: Path absPath = path.toAbsolutePath(); jjg@450: for (Path p: paths) { jjg@450: Path ap = p.toAbsolutePath(); jjg@450: if (absPath.startsWith(ap)) { jjg@450: try { jjg@450: Path rp = ap.relativize(absPath); jjg@450: if (rp != null) // maybe null if absPath same as ap jjg@450: return toBinaryName(rp); jjg@450: } catch (IllegalArgumentException e) { jjg@450: // ignore this p if cannot relativize path to p jjg@450: } jjg@450: } jjg@450: } jjg@450: return null; jjg@450: } jjg@450: }; jjg@450: } jjg@450: jjg@450: protected PathFileObject(JavacPathFileManager fileManager, Path path) { jjg@450: fileManager.getClass(); // null check jjg@450: path.getClass(); // null check jjg@450: this.fileManager = fileManager; jjg@450: this.path = path; jjg@450: } jjg@450: jjg@450: abstract String inferBinaryName(Iterable paths); jjg@450: jjg@450: /** jjg@450: * Return the Path for this object. jjg@450: * @return the Path for this object. jjg@450: */ jjg@450: Path getPath() { jjg@450: return path; jjg@450: } jjg@450: jjg@450: @Override jjg@450: public Kind getKind() { alanb@847: return BaseFileManager.getKind(path.getFileName().toString()); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public boolean isNameCompatible(String simpleName, Kind kind) { jjg@450: simpleName.getClass(); jjg@450: // null check jjg@450: if (kind == Kind.OTHER && getKind() != kind) { jjg@450: return false; jjg@450: } jjg@450: String sn = simpleName + kind.extension; alanb@847: String pn = path.getFileName().toString(); jjg@450: if (pn.equals(sn)) { jjg@450: return true; jjg@450: } jjg@450: if (pn.equalsIgnoreCase(sn)) { jjg@450: try { jjg@450: // allow for Windows alanb@847: return path.toRealPath(false).getFileName().toString().equals(sn); jjg@450: } catch (IOException e) { jjg@450: } jjg@450: } jjg@450: return false; jjg@450: } jjg@450: jjg@450: @Override jjg@450: public NestingKind getNestingKind() { jjg@450: return null; jjg@450: } jjg@450: jjg@450: @Override jjg@450: public Modifier getAccessLevel() { jjg@450: return null; jjg@450: } jjg@450: jjg@450: @Override jjg@450: public URI toUri() { jjg@450: return path.toUri(); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public String getName() { jjg@450: return path.toString(); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public InputStream openInputStream() throws IOException { alanb@847: return Files.newInputStream(path); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public OutputStream openOutputStream() throws IOException { jjg@450: ensureParentDirectoriesExist(); alanb@847: return Files.newOutputStream(path); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public Reader openReader(boolean ignoreEncodingErrors) throws IOException { jjg@450: CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); jjg@450: return new InputStreamReader(openInputStream(), decoder); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { jjg@450: CharBuffer cb = fileManager.getCachedContent(this); jjg@450: if (cb == null) { jjg@450: InputStream in = openInputStream(); jjg@450: try { jjg@450: ByteBuffer bb = fileManager.makeByteBuffer(in); jjg@450: JavaFileObject prev = fileManager.log.useSource(this); jjg@450: try { jjg@450: cb = fileManager.decode(bb, ignoreEncodingErrors); jjg@450: } finally { jjg@450: fileManager.log.useSource(prev); jjg@450: } jjg@450: fileManager.recycleByteBuffer(bb); jjg@450: if (!ignoreEncodingErrors) { jjg@450: fileManager.cache(this, cb); jjg@450: } jjg@450: } finally { jjg@450: in.close(); jjg@450: } jjg@450: } jjg@450: return cb; jjg@450: } jjg@450: jjg@450: @Override jjg@450: public Writer openWriter() throws IOException { jjg@450: ensureParentDirectoriesExist(); alanb@847: return new OutputStreamWriter(Files.newOutputStream(path), fileManager.getEncodingName()); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public long getLastModified() { jjg@450: try { alanb@847: return Files.getLastModifiedTime(path).toMillis(); jjg@450: } catch (IOException e) { jjg@450: return -1; jjg@450: } jjg@450: } jjg@450: jjg@450: @Override jjg@450: public boolean delete() { jjg@450: try { alanb@847: Files.delete(path); jjg@450: return true; jjg@450: } catch (IOException e) { jjg@450: return false; jjg@450: } jjg@450: } jjg@450: jjg@450: public boolean isSameFile(PathFileObject other) { jjg@450: try { alanb@847: return Files.isSameFile(path, other.path); jjg@450: } catch (IOException e) { jjg@450: return false; jjg@450: } jjg@450: } jjg@450: jjg@450: @Override jjg@450: public boolean equals(Object other) { jjg@450: return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public int hashCode() { jjg@450: return path.hashCode(); jjg@450: } jjg@450: jjg@450: @Override jjg@450: public String toString() { jjg@450: return getClass().getSimpleName() + "[" + path + "]"; jjg@450: } jjg@450: jjg@450: private void ensureParentDirectoriesExist() throws IOException { jjg@450: Path parent = path.getParent(); jjg@450: if (parent != null) jjg@450: Files.createDirectories(parent); jjg@450: } jjg@450: jjg@450: private long size() { jjg@450: try { alanb@847: return Files.size(path); jjg@450: } catch (IOException e) { jjg@450: return -1; jjg@450: } jjg@450: } jjg@450: jjg@450: protected static String toBinaryName(Path relativePath) { jjg@450: return toBinaryName(relativePath.toString(), jjg@450: relativePath.getFileSystem().getSeparator()); jjg@450: } jjg@450: jjg@450: protected static String toBinaryName(String relativePath, String sep) { jjg@466: return removeExtension(relativePath).replace(sep, "."); jjg@450: } jjg@450: jjg@450: protected static String removeExtension(String fileName) { jjg@450: int lastDot = fileName.lastIndexOf("."); jjg@450: return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); jjg@450: } jjg@450: }