PathFileObject.java revision 3811:4d4cd7cd731c
145405Smsmith/* 245405Smsmith * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. 345405Smsmith * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 445405Smsmith * 545405Smsmith * This code is free software; you can redistribute it and/or modify it 645405Smsmith * under the terms of the GNU General Public License version 2 only, as 745405Smsmith * published by the Free Software Foundation. Oracle designates this 845405Smsmith * particular file as subject to the "Classpath" exception as provided 945405Smsmith * by Oracle in the LICENSE file that accompanied this code. 1045405Smsmith * 1145405Smsmith * This code is distributed in the hope that it will be useful, but WITHOUT 1245405Smsmith * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1345405Smsmith * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1445405Smsmith * version 2 for more details (a copy is included in the LICENSE file that 1545405Smsmith * accompanied this code). 1645405Smsmith * 1745405Smsmith * You should have received a copy of the GNU General Public License version 1845405Smsmith * 2 along with this work; if not, write to the Free Software Foundation, 1945405Smsmith * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2045405Smsmith * 2145405Smsmith * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2245405Smsmith * or visit www.oracle.com if you need additional information or have any 2345405Smsmith * questions. 2445405Smsmith */ 2545405Smsmith 2646215Smsmithpackage com.sun.tools.javac.file; 2745405Smsmith 2845405Smsmithimport java.io.IOException; 2945405Smsmithimport java.io.InputStream; 3045405Smsmithimport java.io.InputStreamReader; 3145405Smsmithimport java.io.OutputStream; 3245405Smsmithimport java.io.OutputStreamWriter; 3345405Smsmithimport java.io.Reader; 3445405Smsmithimport java.io.Writer; 3545405Smsmithimport java.net.URI; 3645405Smsmithimport java.net.URISyntaxException; 3745405Smsmithimport java.nio.ByteBuffer; 3845405Smsmithimport java.nio.CharBuffer; 3945405Smsmithimport java.nio.charset.CharsetDecoder; 4045405Smsmithimport java.nio.file.FileSystem; 4145405Smsmithimport java.nio.file.FileSystems; 4245405Smsmithimport java.nio.file.Files; 4345405Smsmithimport java.nio.file.LinkOption; 4445405Smsmithimport java.nio.file.Path; 4545405Smsmithimport java.text.Normalizer; 4645405Smsmithimport java.util.Objects; 4745405Smsmith 4845405Smsmithimport javax.lang.model.element.Modifier; 4945405Smsmithimport javax.lang.model.element.NestingKind; 5045405Smsmithimport javax.tools.FileObject; 5145405Smsmithimport javax.tools.JavaFileObject; 5245405Smsmith 5345405Smsmithimport com.sun.tools.javac.file.RelativePath.RelativeFile; 5445405Smsmithimport com.sun.tools.javac.util.DefinedBy; 5545405Smsmithimport com.sun.tools.javac.util.DefinedBy.Api; 5645405Smsmith 5745405Smsmith 5845405Smsmith/** 5945405Smsmith * Implementation of JavaFileObject using java.nio.file API. 6045405Smsmith * 6145405Smsmith * <p>PathFileObjects are, for the most part, straightforward wrappers around 6245405Smsmith * immutable absolute Path objects. Different subtypes are used to provide 6345405Smsmith * specialized implementations of "inferBinaryName" and "getName" that capture 6445405Smsmith * additional information available at the time the object is created. 6546215Smsmith * 6646215Smsmith * <p>In general, {@link JavaFileManager#isSameFile} should be used to 6746215Smsmith * determine whether two file objects refer to the same file on disk. 6846215Smsmith * PathFileObject also supports the standard {@code equals} and {@code hashCode} 6946215Smsmith * methods, primarily for convenience when working with collections. 7045405Smsmith * All of these operations delegate to the equivalent operations on the 7145405Smsmith * underlying Path object. 7245405Smsmith * 7346215Smsmith * <p><b>This is NOT part of any supported API. 7446215Smsmith * If you write code that depends on this, you do so at your own risk. 7545405Smsmith * This code and its internal interfaces are subject to change or 7645405Smsmith * deletion without notice.</b> 7746215Smsmith */ 7846215Smsmithpublic abstract class PathFileObject implements JavaFileObject { 7946215Smsmith private static final FileSystem defaultFileSystem = FileSystems.getDefault(); 8045405Smsmith private static final boolean isMacOS = System.getProperty("os.name", "").contains("OS X"); 8145405Smsmith 8245405Smsmith protected final BaseFileManager fileManager; 8345405Smsmith protected final Path path; 8445405Smsmith private boolean hasParents; 8546215Smsmith 8645405Smsmith /** 8745405Smsmith * Create a PathFileObject for a file within a directory, such that the 8845405Smsmith * binary name can be inferred from the relationship to an enclosing directory. 8945405Smsmith * 9045405Smsmith * The binary name is derived from {@code relativePath}. 9145405Smsmith * The name is derived from the composition of {@code userPackageRootDir} 9245405Smsmith * and {@code relativePath}. 9345405Smsmith * 9445405Smsmith * @param fileManager the file manager creating this file object 9545405Smsmith * @param path the absolute path referred to by this file object 9645405Smsmith * @param userPackageRootDir the path of the directory containing the 9745405Smsmith * root of the package hierarchy 9845405Smsmith * @param relativePath the path of this file relative to {@code userPackageRootDir} 9945405Smsmith */ 10045405Smsmith static PathFileObject forDirectoryPath(BaseFileManager fileManager, Path path, 10145405Smsmith Path userPackageRootDir, RelativePath relativePath) { 10245405Smsmith return new DirectoryFileObject(fileManager, path, userPackageRootDir, relativePath); 10345405Smsmith } 10445405Smsmith 10545405Smsmith private static class DirectoryFileObject extends PathFileObject { 10645405Smsmith private final Path userPackageRootDir; 10745405Smsmith private final RelativePath relativePath; 10845405Smsmith 10945405Smsmith private DirectoryFileObject(BaseFileManager fileManager, Path path, 11045405Smsmith Path userPackageRootDir, RelativePath relativePath) { 11145405Smsmith super(fileManager, path); 11245405Smsmith this.userPackageRootDir = Objects.requireNonNull(userPackageRootDir); 11345405Smsmith this.relativePath = relativePath; 11445405Smsmith } 11545405Smsmith 11645405Smsmith @Override @DefinedBy(Api.COMPILER) 11745405Smsmith public String getName() { 11845405Smsmith return relativePath.resolveAgainst(userPackageRootDir).toString(); 11945405Smsmith } 12045405Smsmith 12145405Smsmith @Override 12245405Smsmith public String inferBinaryName(Iterable<? extends Path> paths) { 12345405Smsmith return toBinaryName(relativePath); 12445405Smsmith } 12545405Smsmith 12645405Smsmith @Override 12745405Smsmith public String toString() { 12845405Smsmith return "DirectoryFileObject[" + userPackageRootDir + ":" + relativePath.path + "]"; 12945405Smsmith } 13045405Smsmith 13145405Smsmith @Override 13245405Smsmith PathFileObject getSibling(String baseName) { 13345405Smsmith return new DirectoryFileObject(fileManager, 13445405Smsmith path.resolveSibling(baseName), 13545405Smsmith userPackageRootDir, 13645405Smsmith new RelativeFile(relativePath.dirname(), baseName) 13745405Smsmith ); 13845405Smsmith } 13945405Smsmith } 14045405Smsmith 14145405Smsmith /** 14245405Smsmith * Create a PathFileObject for a file in a file system such as a jar file, 14345405Smsmith * such that the binary name can be inferred from its position within the 14445405Smsmith * file system. 14545405Smsmith * 14645405Smsmith * The binary name is derived from {@code path}. 14745405Smsmith * The name is derived from the composition of {@code userJarPath} 14845405Smsmith * and {@code path}. 14945405Smsmith * 15045405Smsmith * @param fileManager the file manager creating this file object 15145405Smsmith * @param path the path referred to by this file object 15245405Smsmith * @param userJarPath the path of the jar file containing the file system. 15345405Smsmith */ 15445405Smsmith public static PathFileObject forJarPath(BaseFileManager fileManager, 15545405Smsmith Path path, Path userJarPath) { 15645405Smsmith return new JarFileObject(fileManager, path, userJarPath); 15745405Smsmith } 15845405Smsmith 15945405Smsmith private static class JarFileObject extends PathFileObject { 16045405Smsmith private final Path userJarPath; 16145405Smsmith 16245405Smsmith private JarFileObject(BaseFileManager fileManager, Path path, Path userJarPath) { 16345405Smsmith super(fileManager, path); 16445405Smsmith this.userJarPath = userJarPath; 16545405Smsmith } 16645405Smsmith 16745405Smsmith @Override @DefinedBy(Api.COMPILER) 16845405Smsmith public String getName() { 16945405Smsmith // The use of ( ) to delimit the entry name is not ideal 17045405Smsmith // but it does match earlier behavior 17145405Smsmith return userJarPath + "(" + path + ")"; 17245405Smsmith } 17345405Smsmith 17445405Smsmith @Override 17545405Smsmith public String inferBinaryName(Iterable<? extends Path> paths) { 17645405Smsmith Path root = path.getFileSystem().getRootDirectories().iterator().next(); 17745405Smsmith return toBinaryName(root.relativize(path)); 17845405Smsmith } 17945405Smsmith 18045405Smsmith @Override @DefinedBy(Api.COMPILER) 18145405Smsmith public URI toUri() { 18245405Smsmith // Work around bug JDK-8134451: 18345405Smsmith // path.toUri() returns double-encoded URIs, that cannot be opened by URLConnection 18445405Smsmith return createJarUri(userJarPath, path.toString()); 18545405Smsmith } 18645405Smsmith 18745405Smsmith @Override 18845405Smsmith public String toString() { 18945405Smsmith return "JarFileObject[" + userJarPath + ":" + path + "]"; 19045405Smsmith } 19145405Smsmith 19245405Smsmith @Override 19345405Smsmith PathFileObject getSibling(String baseName) { 19445405Smsmith return new JarFileObject(fileManager, 19545405Smsmith path.resolveSibling(baseName), 19645405Smsmith userJarPath 19745405Smsmith ); 19845405Smsmith } 19945405Smsmith 20045405Smsmith private static URI createJarUri(Path jarFile, String entryName) { 20145405Smsmith URI jarURI = jarFile.toUri().normalize(); 20245405Smsmith String separator = entryName.startsWith("/") ? "!" : "!/"; 20345405Smsmith try { 20445405Smsmith // The jar URI convention appears to be not to re-encode the jarURI 20545405Smsmith return new URI("jar:" + jarURI + separator + entryName); 20645405Smsmith } catch (URISyntaxException e) { 20745405Smsmith throw new CannotCreateUriError(jarURI + separator + entryName, e); 20845405Smsmith } 20945405Smsmith } 21045405Smsmith } 21145405Smsmith 21245405Smsmith /** 21345405Smsmith * Create a PathFileObject for a file in a modular file system, such as jrt:, 21445405Smsmith * such that the binary name can be inferred from its position within the 21545405Smsmith * filesystem. 21645405Smsmith * 21745405Smsmith * The binary name is derived from {@code path}, ignoring the first two 21845405Smsmith * elements of the name (which are "modules" and a module name). 21945405Smsmith * The name is derived from {@code path}. 22045405Smsmith * 22145405Smsmith * @param fileManager the file manager creating this file object 22245405Smsmith * @param path the path referred to by this file object 22345405Smsmith */ 22445405Smsmith public static PathFileObject forJRTPath(BaseFileManager fileManager, 22545405Smsmith final Path path) { 22645405Smsmith return new JRTFileObject(fileManager, path); 22745405Smsmith } 22846215Smsmith 22946215Smsmith private static class JRTFileObject extends PathFileObject { 23046215Smsmith // private final Path javaHome; 23146215Smsmith private JRTFileObject(BaseFileManager fileManager, Path path) { 23245405Smsmith super(fileManager, path); 23345405Smsmith } 23445405Smsmith 23545405Smsmith @Override @DefinedBy(Api.COMPILER) 23645405Smsmith public String getName() { 23745405Smsmith return path.toString(); 23845405Smsmith } 23945405Smsmith 24045405Smsmith @Override 24145405Smsmith public String inferBinaryName(Iterable<? extends Path> paths) { 24245405Smsmith // use subpath to ignore the leading /modules/MODULE-NAME 24345405Smsmith return toBinaryName(path.subpath(2, path.getNameCount())); 24445405Smsmith } 24545405Smsmith 24645405Smsmith @Override 24745405Smsmith public String toString() { 24846215Smsmith return "JRTFileObject[" + path + "]"; 24946215Smsmith } 25046215Smsmith 25146215Smsmith @Override 25246215Smsmith PathFileObject getSibling(String baseName) { 25346215Smsmith return new JRTFileObject(fileManager, 25446215Smsmith path.resolveSibling(baseName) 25546215Smsmith ); 25646215Smsmith } 25746215Smsmith } 25846215Smsmith 25946215Smsmith /** 26046215Smsmith * Create a PathFileObject for a file whose binary name must be inferred 26146215Smsmith * from its position on a search path. 26246215Smsmith * 26346215Smsmith * The binary name is inferred by finding an enclosing directory in 26446215Smsmith * the sequence of paths associated with the location given to 26545405Smsmith * {@link JavaFileManager#inferBinaryName). 26645405Smsmith * The name is derived from {@code userPath}. 26745405Smsmith * 26845405Smsmith * @param fileManager the file manager creating this file object 26945405Smsmith * @param path the path referred to by this file object 27045405Smsmith * @param userPath the "user-friendly" name for this path. 27145405Smsmith */ 27245405Smsmith static PathFileObject forSimplePath(BaseFileManager fileManager, 27345405Smsmith Path path, Path userPath) { 27445405Smsmith return new SimpleFileObject(fileManager, path, userPath); 27545405Smsmith } 27645405Smsmith 27745405Smsmith private static class SimpleFileObject extends PathFileObject { 27845405Smsmith private final Path userPath; 27945405Smsmith private SimpleFileObject(BaseFileManager fileManager, Path path, Path userPath) { 28045405Smsmith super(fileManager, path); 28145405Smsmith this.userPath = userPath; 28245405Smsmith } 28345405Smsmith 28445405Smsmith @Override @DefinedBy(Api.COMPILER) 28545405Smsmith public String getName() { 28645405Smsmith return userPath.toString(); 28745405Smsmith } 28845405Smsmith 28945405Smsmith @Override 29045405Smsmith public String inferBinaryName(Iterable<? extends Path> paths) { 29145405Smsmith Path absPath = path.toAbsolutePath(); 29245405Smsmith for (Path p: paths) { 29345405Smsmith Path ap = p.toAbsolutePath(); 29445405Smsmith if (absPath.startsWith(ap)) { 29545405Smsmith try { 29645405Smsmith Path rp = ap.relativize(absPath); 29745405Smsmith if (rp != null) // maybe null if absPath same as ap 29845405Smsmith return toBinaryName(rp); 29945405Smsmith } catch (IllegalArgumentException e) { 30045405Smsmith // ignore this p if cannot relativize path to p 30145405Smsmith } 30245405Smsmith } 30345405Smsmith } 30445405Smsmith return null; 30545405Smsmith } 30645405Smsmith 30745405Smsmith @Override 30845405Smsmith PathFileObject getSibling(String baseName) { 30945405Smsmith return new SimpleFileObject(fileManager, 31045405Smsmith path.resolveSibling(baseName), 31145405Smsmith userPath.resolveSibling(baseName) 31245405Smsmith ); 31345405Smsmith } 31445405Smsmith } 31545405Smsmith 31645405Smsmith /** 31745405Smsmith * Create a PathFileObject, for a specified path, in the context of 31845405Smsmith * a given file manager. 31945405Smsmith * 32045405Smsmith * In general, this path should be an 32145405Smsmith * {@link Path#toAbsolutePath absolute path}, if not a 32245405Smsmith * {@link Path#toRealPath} real path. 32345405Smsmith * It will be used as the basis of {@code equals}, {@code hashCode} 32445405Smsmith * and {@code isSameFile} methods on this file object. 32545405Smsmith * 32645405Smsmith * A PathFileObject should also have a "friendly name" per the 32745405Smsmith * specification for {@link FileObject#getName}. The friendly name 32845405Smsmith * is provided by the various subtypes of {@code PathFileObject}. 32945405Smsmith * 33045405Smsmith * @param fileManager the file manager creating this file object 33145405Smsmith * @param path the path contained in this file object. 33245405Smsmith */ 33345405Smsmith protected PathFileObject(BaseFileManager fileManager, Path path) { 33445405Smsmith this.fileManager = Objects.requireNonNull(fileManager); 33545405Smsmith if (Files.isDirectory(path)) { 33645405Smsmith throw new IllegalArgumentException("directories not supported"); 33745405Smsmith } 33845405Smsmith this.path = path; 33945405Smsmith } 34045405Smsmith 34145405Smsmith /** 34245405Smsmith * See {@link JavacFileManager#inferBinaryName}. 34345405Smsmith */ 34445405Smsmith abstract String inferBinaryName(Iterable<? extends Path> paths); 34545405Smsmith 34645405Smsmith /** 34745405Smsmith * Return the file object for a sibling file with a given file name. 34845405Smsmith * See {@link JavacFileManager#getFileForOutput} and 34945405Smsmith * {@link JavacFileManager#getJavaFileForOutput}. 35045405Smsmith */ 35145405Smsmith abstract PathFileObject getSibling(String basename); 35245405Smsmith 35345405Smsmith /** 35445405Smsmith * Return the Path for this object. 35545405Smsmith * @return the Path for this object. 35645405Smsmith * @see StandardJavaFileManager#asPath 35745405Smsmith */ 35845405Smsmith public Path getPath() { 35945405Smsmith return path; 36045405Smsmith } 36145405Smsmith 36245405Smsmith /** 36345405Smsmith * The short name is used when generating raw diagnostics. 36445405Smsmith * @return the last component of the path 36545405Smsmith */ 36645405Smsmith public String getShortName() { 36745405Smsmith return path.getFileName().toString(); 36845405Smsmith } 36945405Smsmith 37045405Smsmith @Override @DefinedBy(Api.COMPILER) 37145405Smsmith public Kind getKind() { 37245405Smsmith return BaseFileManager.getKind(path.getFileName().toString()); 37345405Smsmith } 37445405Smsmith 37545405Smsmith @Override @DefinedBy(Api.COMPILER) 37645405Smsmith public boolean isNameCompatible(String simpleName, Kind kind) { 37745405Smsmith Objects.requireNonNull(simpleName); 37845405Smsmith Objects.requireNonNull(kind); 37945405Smsmith 38045405Smsmith if (kind == Kind.OTHER && getKind() != kind) { 38145405Smsmith return false; 38245405Smsmith } 38345405Smsmith 38445405Smsmith String sn = simpleName + kind.extension; 38545405Smsmith String pn = path.getFileName().toString(); 38645405Smsmith if (pn.equals(sn)) { 38745405Smsmith return true; 38845405Smsmith } 38945405Smsmith 39045405Smsmith if (path.getFileSystem() == defaultFileSystem) { 39145405Smsmith if (isMacOS) { 39245405Smsmith String name = path.getFileName().toString(); 39345405Smsmith if (Normalizer.isNormalized(name, Normalizer.Form.NFD) 39445405Smsmith && Normalizer.isNormalized(sn, Normalizer.Form.NFC)) { 39545405Smsmith // On Mac OS X it is quite possible to have the file name and the 39645405Smsmith // given simple name normalized in different ways. 39745405Smsmith // In that case we have to normalize file name to the 39845405Smsmith // Normal Form Composed (NFC). 39945405Smsmith String normName = Normalizer.normalize(name, Normalizer.Form.NFC); 40045405Smsmith if (normName.equals(sn)) { 40145405Smsmith return true; 40245405Smsmith } 40345405Smsmith } 40445405Smsmith } 40545405Smsmith 40645405Smsmith if (pn.equalsIgnoreCase(sn)) { 40745405Smsmith try { 40845405Smsmith // allow for Windows 40945405Smsmith return path.toRealPath(LinkOption.NOFOLLOW_LINKS).getFileName().toString().equals(sn); 41045405Smsmith } catch (IOException e) { 41145405Smsmith } 41245405Smsmith } 41345405Smsmith } 41445405Smsmith 41545405Smsmith return false; 41645405Smsmith } 41745405Smsmith 41845405Smsmith @Override @DefinedBy(Api.COMPILER) 41945405Smsmith public NestingKind getNestingKind() { 42045405Smsmith return null; 42145405Smsmith } 42245405Smsmith 42345405Smsmith @Override @DefinedBy(Api.COMPILER) 42445405Smsmith public Modifier getAccessLevel() { 42545405Smsmith return null; 42645405Smsmith } 42745405Smsmith 42845405Smsmith @Override @DefinedBy(Api.COMPILER) 42945405Smsmith public URI toUri() { 43045405Smsmith return path.toUri(); 43145405Smsmith } 43245405Smsmith 43345405Smsmith @Override @DefinedBy(Api.COMPILER) 43445405Smsmith public InputStream openInputStream() throws IOException { 43545405Smsmith fileManager.updateLastUsedTime(); 43645405Smsmith return Files.newInputStream(path); 43745405Smsmith } 43845405Smsmith 43945405Smsmith @Override @DefinedBy(Api.COMPILER) 44045405Smsmith public OutputStream openOutputStream() throws IOException { 44145405Smsmith fileManager.updateLastUsedTime(); 44245405Smsmith fileManager.flushCache(this); 44345405Smsmith ensureParentDirectoriesExist(); 44445405Smsmith return Files.newOutputStream(path); 44545405Smsmith } 44645405Smsmith 44745405Smsmith @Override @DefinedBy(Api.COMPILER) 44845405Smsmith public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 44945405Smsmith CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); 45045405Smsmith return new InputStreamReader(openInputStream(), decoder); 45145405Smsmith } 45245405Smsmith 45345405Smsmith @Override @DefinedBy(Api.COMPILER) 45445405Smsmith public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 45545405Smsmith CharBuffer cb = fileManager.getCachedContent(this); 45645405Smsmith if (cb == null) { 45745405Smsmith try (InputStream in = openInputStream()) { 45845405Smsmith ByteBuffer bb = fileManager.makeByteBuffer(in); 45945405Smsmith JavaFileObject prev = fileManager.log.useSource(this); 46045405Smsmith try { 46145405Smsmith cb = fileManager.decode(bb, ignoreEncodingErrors); 46245405Smsmith } finally { 46345405Smsmith fileManager.log.useSource(prev); 46445405Smsmith } 46545405Smsmith fileManager.recycleByteBuffer(bb); 46645405Smsmith if (!ignoreEncodingErrors) { 46745405Smsmith fileManager.cache(this, cb); 46845405Smsmith } 46945405Smsmith } 47045405Smsmith } 47145405Smsmith return cb; 47245405Smsmith } 47345405Smsmith 47445405Smsmith @Override @DefinedBy(Api.COMPILER) 47545405Smsmith public Writer openWriter() throws IOException { 47645405Smsmith fileManager.updateLastUsedTime(); 47745405Smsmith fileManager.flushCache(this); 47845405Smsmith ensureParentDirectoriesExist(); 47945405Smsmith return new OutputStreamWriter(Files.newOutputStream(path), fileManager.getEncodingName()); 48045405Smsmith } 48145405Smsmith 48245405Smsmith @Override @DefinedBy(Api.COMPILER) 48345405Smsmith public long getLastModified() { 48445405Smsmith try { 48545405Smsmith return Files.getLastModifiedTime(path).toMillis(); 48645405Smsmith } catch (IOException e) { 48745405Smsmith return 0; 48845405Smsmith } 48945405Smsmith } 49045405Smsmith 49145405Smsmith @Override @DefinedBy(Api.COMPILER) 49245405Smsmith public boolean delete() { 49345405Smsmith try { 49445405Smsmith Files.delete(path); 49545405Smsmith return true; 49645405Smsmith } catch (IOException e) { 49745405Smsmith return false; 49845405Smsmith } 49945405Smsmith } 50045405Smsmith 50145405Smsmith boolean isSameFile(PathFileObject other) { 50245405Smsmith // By construction, the "path" field should be canonical in all likely, supported scenarios. 50345405Smsmith // (Any exceptions would involve the use of symlinks within a package hierarchy.) 50445405Smsmith // Therefore, it is sufficient to check that the paths are .equals. 50545405Smsmith return path.equals(other.path); 50645405Smsmith } 50745405Smsmith 50845405Smsmith @Override 50945405Smsmith public boolean equals(Object other) { 51045405Smsmith return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); 51145405Smsmith } 51246215Smsmith 51346215Smsmith @Override 51446215Smsmith public int hashCode() { 51546215Smsmith return path.hashCode(); 51646215Smsmith } 51746215Smsmith 51845405Smsmith @Override 51945405Smsmith public String toString() { 52045405Smsmith return getClass().getSimpleName() + "[" + path + "]"; 52145405Smsmith } 52245405Smsmith 52345405Smsmith private void ensureParentDirectoriesExist() throws IOException { 52445405Smsmith if (!hasParents) { 52545405Smsmith Path parent = path.getParent(); 52645405Smsmith if (parent != null && !Files.isDirectory(parent)) { 52745405Smsmith try { 52845405Smsmith Files.createDirectories(parent); 52945405Smsmith } catch (IOException e) { 53045405Smsmith throw new IOException("could not create parent directories", e); 53145405Smsmith } 53245405Smsmith } 53345405Smsmith hasParents = true; 53445405Smsmith } 53545405Smsmith } 53645405Smsmith 53745405Smsmith protected static String toBinaryName(RelativePath relativePath) { 53845405Smsmith return toBinaryName(relativePath.path, "/"); 53945405Smsmith } 54045405Smsmith 54145405Smsmith protected static String toBinaryName(Path relativePath) { 54245405Smsmith return toBinaryName(relativePath.toString(), 54345405Smsmith relativePath.getFileSystem().getSeparator()); 54445405Smsmith } 54545405Smsmith 54645405Smsmith private static String toBinaryName(String relativePath, String sep) { 54745405Smsmith return removeExtension(relativePath).replace(sep, "."); 54845405Smsmith } 54945405Smsmith 55045405Smsmith private static String removeExtension(String fileName) { 55145405Smsmith int lastDot = fileName.lastIndexOf("."); 55245405Smsmith return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); 55345405Smsmith } 55445405Smsmith 55545405Smsmith /** Return the last component of a presumed hierarchical URI. 55645405Smsmith * From the scheme specific part of the URI, it returns the substring 55745405Smsmith * after the last "/" if any, or everything if no "/" is found. 55845405Smsmith */ 55945405Smsmith public static String getSimpleName(FileObject fo) { 56045405Smsmith URI uri = fo.toUri(); 56145405Smsmith String s = uri.getSchemeSpecificPart(); 56245405Smsmith return s.substring(s.lastIndexOf("/") + 1); // safe when / not found 56345405Smsmith 56446215Smsmith } 56546215Smsmith 56646215Smsmith /** Used when URLSyntaxException is thrown unexpectedly during 56745405Smsmith * implementations of FileObject.toURI(). */ 56846215Smsmith public static class CannotCreateUriError extends Error { 56946215Smsmith private static final long serialVersionUID = 9101708840997613546L; 57046215Smsmith public CannotCreateUriError(String value, Throwable cause) { 57146215Smsmith super(value, cause); 57246215Smsmith } 57346215Smsmith } 57446215Smsmith} 57545405Smsmith