893N/A/*
3909N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
893N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
893N/A *
893N/A * This code is free software; you can redistribute it and/or modify it
893N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
893N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
893N/A *
893N/A * This code is distributed in the hope that it will be useful, but WITHOUT
893N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
893N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
893N/A * version 2 for more details (a copy is included in the LICENSE file that
893N/A * accompanied this code).
893N/A *
893N/A * You should have received a copy of the GNU General Public License version
893N/A * 2 along with this work; if not, write to the Free Software Foundation,
893N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
893N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
893N/A */
893N/A
893N/Apackage sun.nio.fs;
893N/A
893N/Aimport java.nio.file.*;
893N/Aimport java.nio.file.attribute.*;
893N/Aimport java.io.*;
893N/Aimport java.net.URI;
893N/Aimport java.util.*;
893N/Aimport java.lang.ref.WeakReference;
893N/A
893N/Aimport com.sun.nio.file.ExtendedWatchEventModifier;
893N/A
893N/Aimport static sun.nio.fs.WindowsNativeDispatcher.*;
893N/Aimport static sun.nio.fs.WindowsConstants.*;
893N/A
893N/A/**
893N/A * Windows implementation of Path
893N/A */
893N/A
893N/Aclass WindowsPath extends AbstractPath {
893N/A
893N/A // The maximum path that does not require long path prefix. On Windows
893N/A // the maximum path is 260 minus 1 (NUL) but for directories it is 260
893N/A // minus 12 minus 1 (to allow for the creation of a 8.3 file in the
893N/A // directory).
893N/A private static final int MAX_PATH = 247;
893N/A
893N/A // Maximum extended-length path
893N/A private static final int MAX_LONG_PATH = 32000;
893N/A
893N/A // FIXME - eliminate this reference to reduce space
893N/A private final WindowsFileSystem fs;
893N/A
893N/A // path type
893N/A private final WindowsPathType type;
893N/A // root component (may be empty)
893N/A private final String root;
893N/A // normalized path
893N/A private final String path;
893N/A
893N/A // the path to use in Win32 calls. This differs from path for relative
893N/A // paths and has a long path prefix for all paths longer than MAX_PATH.
893N/A private volatile WeakReference<String> pathForWin32Calls;
893N/A
893N/A // offsets into name components (computed lazily)
893N/A private volatile Integer[] offsets;
893N/A
893N/A // computed hash code (computed lazily, no need to be volatile)
893N/A private int hash;
893N/A
893N/A
893N/A /**
893N/A * Initializes a new instance of this class.
893N/A */
907N/A private WindowsPath(WindowsFileSystem fs,
907N/A WindowsPathType type,
907N/A String root,
907N/A String path)
893N/A {
893N/A this.fs = fs;
893N/A this.type = type;
893N/A this.root = root;
893N/A this.path = path;
893N/A }
893N/A
893N/A /**
907N/A * Creates a Path by parsing the given path.
893N/A */
893N/A static WindowsPath parse(WindowsFileSystem fs, String path) {
893N/A WindowsPathParser.Result result = WindowsPathParser.parse(path);
893N/A return new WindowsPath(fs, result.type(), result.root(), result.path());
893N/A }
893N/A
893N/A /**
907N/A * Creates a Path from a given path that is known to be normalized.
893N/A */
907N/A static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
907N/A String path,
907N/A BasicFileAttributes attrs)
907N/A {
893N/A try {
893N/A WindowsPathParser.Result result =
893N/A WindowsPathParser.parseNormalizedPath(path);
907N/A if (attrs == null) {
907N/A return new WindowsPath(fs,
907N/A result.type(),
907N/A result.root(),
907N/A result.path());
907N/A } else {
907N/A return new WindowsPathWithAttributes(fs,
907N/A result.type(),
907N/A result.root(),
907N/A result.path(),
907N/A attrs);
907N/A }
893N/A } catch (InvalidPathException x) {
893N/A throw new AssertionError(x.getMessage());
893N/A }
893N/A }
893N/A
907N/A /**
907N/A * Creates a WindowsPath from a given path that is known to be normalized.
907N/A */
907N/A static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
907N/A String path)
907N/A {
907N/A return createFromNormalizedPath(fs, path, null);
907N/A }
907N/A
907N/A /**
907N/A * Special implementation with attached/cached attributes (used to quicken
907N/A * file tree traveral)
907N/A */
907N/A private static class WindowsPathWithAttributes
907N/A extends WindowsPath implements BasicFileAttributesHolder
907N/A {
907N/A final WeakReference<BasicFileAttributes> ref;
907N/A
907N/A WindowsPathWithAttributes(WindowsFileSystem fs,
907N/A WindowsPathType type,
907N/A String root,
907N/A String path,
907N/A BasicFileAttributes attrs)
907N/A {
907N/A super(fs, type, root, path);
907N/A ref = new WeakReference<BasicFileAttributes>(attrs);
907N/A }
907N/A
907N/A @Override
907N/A public BasicFileAttributes get() {
907N/A return ref.get();
907N/A }
907N/A
907N/A @Override
907N/A public void invalidate() {
907N/A ref.clear();
907N/A }
1319N/A
1319N/A // no need to override equals/hashCode.
907N/A }
907N/A
893N/A // use this message when throwing exceptions
893N/A String getPathForExceptionMessage() {
893N/A return path;
893N/A }
893N/A
893N/A // use this path for permission checks
893N/A String getPathForPermissionCheck() {
893N/A return path;
893N/A }
893N/A
893N/A // use this path for Win32 calls
893N/A // This method will prefix long paths with \\?\ or \\?\UNC as required.
893N/A String getPathForWin32Calls() throws WindowsException {
893N/A // short absolute paths can be used directly
893N/A if (isAbsolute() && path.length() <= MAX_PATH)
893N/A return path;
893N/A
893N/A // return cached values if available
893N/A WeakReference<String> ref = pathForWin32Calls;
893N/A String resolved = (ref != null) ? ref.get() : null;
893N/A if (resolved != null) {
893N/A // Win32 path already available
893N/A return resolved;
893N/A }
893N/A
893N/A // resolve against default directory
893N/A resolved = getAbsolutePath();
893N/A
893N/A // Long paths need to have "." and ".." removed and be prefixed with
893N/A // "\\?\". Note that it is okay to remove ".." even when it follows
893N/A // a link - for example, it is okay for foo/link/../bar to be changed
893N/A // to foo/bar. The reason is that Win32 APIs to access foo/link/../bar
893N/A // will access foo/bar anyway (which differs to Unix systems)
893N/A if (resolved.length() > MAX_PATH) {
893N/A if (resolved.length() > MAX_LONG_PATH) {
893N/A throw new WindowsException("Cannot access file with path exceeding "
893N/A + MAX_LONG_PATH + " characters");
893N/A }
893N/A resolved = addPrefixIfNeeded(GetFullPathName(resolved));
893N/A }
893N/A
893N/A // cache the resolved path (except drive relative paths as the working
893N/A // directory on removal media devices can change during the lifetime
893N/A // of the VM)
893N/A if (type != WindowsPathType.DRIVE_RELATIVE) {
893N/A synchronized (path) {
893N/A pathForWin32Calls = new WeakReference<String>(resolved);
893N/A }
893N/A }
893N/A return resolved;
893N/A }
893N/A
893N/A // return this path resolved against the file system's default directory
893N/A private String getAbsolutePath() throws WindowsException {
893N/A if (isAbsolute())
893N/A return path;
893N/A
893N/A // Relative path ("foo" for example)
893N/A if (type == WindowsPathType.RELATIVE) {
893N/A String defaultDirectory = getFileSystem().defaultDirectory();
3471N/A if (isEmpty())
3471N/A return defaultDirectory;
893N/A if (defaultDirectory.endsWith("\\")) {
893N/A return defaultDirectory + path;
893N/A } else {
893N/A StringBuilder sb =
893N/A new StringBuilder(defaultDirectory.length() + path.length() + 1);
893N/A return sb.append(defaultDirectory).append('\\').append(path).toString();
893N/A }
893N/A }
893N/A
893N/A // Directory relative path ("\foo" for example)
893N/A if (type == WindowsPathType.DIRECTORY_RELATIVE) {
893N/A String defaultRoot = getFileSystem().defaultRoot();
893N/A return defaultRoot + path.substring(1);
893N/A }
893N/A
893N/A // Drive relative path ("C:foo" for example).
893N/A if (isSameDrive(root, getFileSystem().defaultRoot())) {
893N/A // relative to default directory
893N/A String remaining = path.substring(root.length());
893N/A String defaultDirectory = getFileSystem().defaultDirectory();
893N/A String result;
893N/A if (defaultDirectory.endsWith("\\")) {
893N/A result = defaultDirectory + remaining;
893N/A } else {
893N/A result = defaultDirectory + "\\" + remaining;
893N/A }
893N/A return result;
893N/A } else {
893N/A // relative to some other drive
893N/A String wd;
893N/A try {
893N/A int dt = GetDriveType(root + "\\");
893N/A if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR)
893N/A throw new WindowsException("");
893N/A wd = GetFullPathName(root + ".");
893N/A } catch (WindowsException x) {
893N/A throw new WindowsException("Unable to get working directory of drive '" +
893N/A Character.toUpperCase(root.charAt(0)) + "'");
893N/A }
893N/A String result = wd;
893N/A if (wd.endsWith("\\")) {
893N/A result += path.substring(root.length());
893N/A } else {
893N/A if (path.length() > root.length())
893N/A result += "\\" + path.substring(root.length());
893N/A }
893N/A return result;
893N/A }
893N/A }
893N/A
893N/A // returns true if same drive letter
893N/A private static boolean isSameDrive(String root1, String root2) {
893N/A return Character.toUpperCase(root1.charAt(0)) ==
893N/A Character.toUpperCase(root2.charAt(0));
893N/A }
893N/A
893N/A // Add long path prefix to path if required
3471N/A static String addPrefixIfNeeded(String path) {
6140N/A if (path.length() > MAX_PATH) {
893N/A if (path.startsWith("\\\\")) {
893N/A path = "\\\\?\\UNC" + path.substring(1, path.length());
893N/A } else {
893N/A path = "\\\\?\\" + path;
893N/A }
893N/A }
893N/A return path;
893N/A }
893N/A
893N/A @Override
893N/A public WindowsFileSystem getFileSystem() {
893N/A return fs;
893N/A }
893N/A
893N/A // -- Path operations --
893N/A
3471N/A private boolean isEmpty() {
3471N/A return path.length() == 0;
3471N/A }
3471N/A
3471N/A private WindowsPath emptyPath() {
3471N/A return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", "");
3471N/A }
3471N/A
893N/A @Override
3471N/A public Path getFileName() {
3471N/A int len = path.length();
3471N/A // represents empty path
3471N/A if (len == 0)
3471N/A return this;
893N/A // represents root component only
3471N/A if (root.length() == len)
893N/A return null;
893N/A int off = path.lastIndexOf('\\');
893N/A if (off < root.length())
893N/A off = root.length();
893N/A else
893N/A off++;
893N/A return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off));
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath getParent() {
893N/A // represents root component only
893N/A if (root.length() == path.length())
893N/A return null;
893N/A int off = path.lastIndexOf('\\');
893N/A if (off < root.length())
893N/A return getRoot();
893N/A else
893N/A return new WindowsPath(getFileSystem(),
893N/A type,
893N/A root,
893N/A path.substring(0, off));
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath getRoot() {
893N/A if (root.length() == 0)
893N/A return null;
893N/A return new WindowsPath(getFileSystem(), type, root, root);
893N/A }
893N/A
893N/A // package-private
3471N/A WindowsPathType type() {
3471N/A return type;
3471N/A }
3471N/A
3471N/A // package-private
893N/A boolean isUnc() {
893N/A return type == WindowsPathType.UNC;
893N/A }
893N/A
907N/A boolean needsSlashWhenResolving() {
907N/A if (path.endsWith("\\"))
907N/A return false;
907N/A return path.length() > root.length();
907N/A }
907N/A
893N/A @Override
893N/A public boolean isAbsolute() {
893N/A return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
893N/A }
893N/A
3471N/A static WindowsPath toWindowsPath(Path path) {
893N/A if (path == null)
893N/A throw new NullPointerException();
893N/A if (!(path instanceof WindowsPath)) {
893N/A throw new ProviderMismatchException();
893N/A }
893N/A return (WindowsPath)path;
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath relativize(Path obj) {
3471N/A WindowsPath other = toWindowsPath(obj);
893N/A if (this.equals(other))
3471N/A return emptyPath();
893N/A
893N/A // can only relativize paths of the same type
893N/A if (this.type != other.type)
893N/A throw new IllegalArgumentException("'other' is different type of Path");
893N/A
893N/A // can only relativize paths if root component matches
893N/A if (!this.root.equalsIgnoreCase(other.root))
893N/A throw new IllegalArgumentException("'other' has different root");
893N/A
893N/A int bn = this.getNameCount();
893N/A int cn = other.getNameCount();
893N/A
893N/A // skip matching names
893N/A int n = (bn > cn) ? cn : bn;
893N/A int i = 0;
893N/A while (i < n) {
893N/A if (!this.getName(i).equals(other.getName(i)))
893N/A break;
893N/A i++;
893N/A }
893N/A
893N/A // append ..\ for remaining names in the base
893N/A StringBuilder result = new StringBuilder();
893N/A for (int j=i; j<bn; j++) {
893N/A result.append("..\\");
893N/A }
893N/A
893N/A // append remaining names in child
893N/A for (int j=i; j<cn; j++) {
893N/A result.append(other.getName(j).toString());
893N/A result.append("\\");
893N/A }
893N/A
893N/A // drop trailing slash in result
893N/A result.setLength(result.length()-1);
893N/A return createFromNormalizedPath(getFileSystem(), result.toString());
893N/A }
893N/A
893N/A @Override
893N/A public Path normalize() {
893N/A final int count = getNameCount();
3471N/A if (count == 0 || isEmpty())
893N/A return this;
893N/A
893N/A boolean[] ignore = new boolean[count]; // true => ignore name
893N/A int remaining = count; // number of names remaining
893N/A
893N/A // multiple passes to eliminate all occurences of "." and "name/.."
893N/A int prevRemaining;
893N/A do {
893N/A prevRemaining = remaining;
893N/A int prevName = -1;
893N/A for (int i=0; i<count; i++) {
893N/A if (ignore[i])
893N/A continue;
893N/A
893N/A String name = elementAsString(i);
893N/A
893N/A // not "." or ".."
893N/A if (name.length() > 2) {
893N/A prevName = i;
893N/A continue;
893N/A }
893N/A
893N/A // "." or something else
893N/A if (name.length() == 1) {
893N/A // ignore "."
893N/A if (name.charAt(0) == '.') {
893N/A ignore[i] = true;
893N/A remaining--;
893N/A } else {
893N/A prevName = i;
893N/A }
893N/A continue;
893N/A }
893N/A
893N/A // not ".."
893N/A if (name.charAt(0) != '.' || name.charAt(1) != '.') {
893N/A prevName = i;
893N/A continue;
893N/A }
893N/A
893N/A // ".." found
893N/A if (prevName >= 0) {
893N/A // name/<ignored>/.. found so mark name and ".." to be
893N/A // ignored
893N/A ignore[prevName] = true;
893N/A ignore[i] = true;
893N/A remaining = remaining - 2;
893N/A prevName = -1;
893N/A } else {
893N/A // Cases:
893N/A // C:\<ignored>\..
893N/A // \\server\\share\<ignored>\..
893N/A // \<ignored>..
893N/A if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) {
893N/A boolean hasPrevious = false;
893N/A for (int j=0; j<i; j++) {
893N/A if (!ignore[j]) {
893N/A hasPrevious = true;
893N/A break;
893N/A }
893N/A }
893N/A if (!hasPrevious) {
893N/A // all proceeding names are ignored
893N/A ignore[i] = true;
893N/A remaining--;
893N/A }
893N/A }
893N/A }
893N/A }
893N/A } while (prevRemaining > remaining);
893N/A
893N/A // no redundant names
893N/A if (remaining == count)
893N/A return this;
893N/A
893N/A // corner case - all names removed
893N/A if (remaining == 0) {
3471N/A return (root.length() == 0) ? emptyPath() : getRoot();
893N/A }
893N/A
893N/A // re-constitute the path from the remaining names.
893N/A StringBuilder result = new StringBuilder();
893N/A if (root != null)
893N/A result.append(root);
893N/A for (int i=0; i<count; i++) {
893N/A if (!ignore[i]) {
3471N/A result.append(getName(i));
893N/A result.append("\\");
893N/A }
893N/A }
893N/A
893N/A // drop trailing slash in result
893N/A result.setLength(result.length()-1);
893N/A return createFromNormalizedPath(getFileSystem(), result.toString());
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath resolve(Path obj) {
3471N/A WindowsPath other = toWindowsPath(obj);
3471N/A if (other.isEmpty())
893N/A return this;
893N/A if (other.isAbsolute())
893N/A return other;
893N/A
893N/A switch (other.type) {
893N/A case RELATIVE: {
893N/A String result;
893N/A if (path.endsWith("\\") || (root.length() == path.length())) {
893N/A result = path + other.path;
893N/A } else {
893N/A result = path + "\\" + other.path;
893N/A }
893N/A return new WindowsPath(getFileSystem(), type, root, result);
893N/A }
893N/A
893N/A case DIRECTORY_RELATIVE: {
893N/A String result;
893N/A if (root.endsWith("\\")) {
893N/A result = root + other.path.substring(1);
893N/A } else {
893N/A result = root + other.path;
893N/A }
893N/A return createFromNormalizedPath(getFileSystem(), result);
893N/A }
893N/A
893N/A case DRIVE_RELATIVE: {
893N/A if (!root.endsWith("\\"))
893N/A return other;
893N/A // if different roots then return other
893N/A String thisRoot = root.substring(0, root.length()-1);
893N/A if (!thisRoot.equalsIgnoreCase(other.root))
893N/A return other;
893N/A // same roots
893N/A String remaining = other.path.substring(other.root.length());
893N/A String result;
893N/A if (path.endsWith("\\")) {
893N/A result = path + remaining;
893N/A } else {
893N/A result = path + "\\" + remaining;
893N/A }
893N/A return createFromNormalizedPath(getFileSystem(), result);
893N/A }
893N/A
893N/A default:
893N/A throw new AssertionError();
893N/A }
893N/A }
893N/A
893N/A // generate offset array
893N/A private void initOffsets() {
893N/A if (offsets == null) {
3471N/A ArrayList<Integer> list = new ArrayList<>();
3471N/A if (isEmpty()) {
3471N/A // empty path considered to have one name element
3471N/A list.add(0);
3471N/A } else {
3471N/A int start = root.length();
3471N/A int off = root.length();
3471N/A while (off < path.length()) {
3471N/A if (path.charAt(off) != '\\') {
3471N/A off++;
3471N/A } else {
3471N/A list.add(start);
3471N/A start = ++off;
3471N/A }
3471N/A }
3471N/A if (start != off)
893N/A list.add(start);
893N/A }
893N/A synchronized (this) {
893N/A if (offsets == null)
893N/A offsets = list.toArray(new Integer[list.size()]);
893N/A }
893N/A }
893N/A }
893N/A
893N/A @Override
893N/A public int getNameCount() {
893N/A initOffsets();
893N/A return offsets.length;
893N/A }
893N/A
893N/A private String elementAsString(int i) {
893N/A initOffsets();
893N/A if (i == (offsets.length-1))
893N/A return path.substring(offsets[i]);
893N/A return path.substring(offsets[i], offsets[i+1]-1);
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath getName(int index) {
893N/A initOffsets();
893N/A if (index < 0 || index >= offsets.length)
893N/A throw new IllegalArgumentException();
893N/A return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index));
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath subpath(int beginIndex, int endIndex) {
893N/A initOffsets();
893N/A if (beginIndex < 0)
893N/A throw new IllegalArgumentException();
893N/A if (beginIndex >= offsets.length)
893N/A throw new IllegalArgumentException();
893N/A if (endIndex > offsets.length)
893N/A throw new IllegalArgumentException();
893N/A if (beginIndex >= endIndex)
893N/A throw new IllegalArgumentException();
893N/A
893N/A StringBuilder sb = new StringBuilder();
893N/A Integer[] nelems = new Integer[endIndex - beginIndex];
893N/A for (int i = beginIndex; i < endIndex; i++) {
893N/A nelems[i-beginIndex] = sb.length();
893N/A sb.append(elementAsString(i));
893N/A if (i != (endIndex-1))
893N/A sb.append("\\");
893N/A }
893N/A return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());
893N/A }
893N/A
893N/A @Override
893N/A public boolean startsWith(Path obj) {
3779N/A if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
3779N/A return false;
3779N/A WindowsPath other = (WindowsPath)obj;
893N/A
893N/A // if this path has a root component the given path's root must match
3471N/A if (!this.root.equalsIgnoreCase(other.root)) {
893N/A return false;
3471N/A }
3471N/A
3471N/A // empty path starts with itself
3471N/A if (other.isEmpty())
3471N/A return this.isEmpty();
893N/A
893N/A // roots match so compare elements
893N/A int thisCount = getNameCount();
893N/A int otherCount = other.getNameCount();
893N/A if (otherCount <= thisCount) {
893N/A while (--otherCount >= 0) {
893N/A String thisElement = this.elementAsString(otherCount);
893N/A String otherElement = other.elementAsString(otherCount);
893N/A // FIXME: should compare in uppercase
893N/A if (!thisElement.equalsIgnoreCase(otherElement))
893N/A return false;
893N/A }
893N/A return true;
893N/A }
893N/A return false;
893N/A }
893N/A
893N/A @Override
893N/A public boolean endsWith(Path obj) {
3779N/A if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
3779N/A return false;
3779N/A WindowsPath other = (WindowsPath)obj;
893N/A
893N/A // other path is longer
3471N/A if (other.path.length() > this.path.length()) {
893N/A return false;
893N/A }
893N/A
3471N/A // empty path ends in itself
3471N/A if (other.isEmpty()) {
3471N/A return this.isEmpty();
3471N/A }
3471N/A
893N/A int thisCount = this.getNameCount();
893N/A int otherCount = other.getNameCount();
893N/A
893N/A // given path has more elements that this path
893N/A if (otherCount > thisCount) {
893N/A return false;
893N/A }
893N/A
893N/A // compare roots
893N/A if (other.root.length() > 0) {
893N/A if (otherCount < thisCount)
893N/A return false;
893N/A // FIXME: should compare in uppercase
893N/A if (!this.root.equalsIgnoreCase(other.root))
893N/A return false;
893N/A }
893N/A
893N/A // match last 'otherCount' elements
893N/A int off = thisCount - otherCount;
893N/A while (--otherCount >= 0) {
893N/A String thisElement = this.elementAsString(off + otherCount);
893N/A String otherElement = other.elementAsString(otherCount);
893N/A // FIXME: should compare in uppercase
893N/A if (!thisElement.equalsIgnoreCase(otherElement))
893N/A return false;
893N/A }
893N/A return true;
893N/A }
893N/A
893N/A @Override
893N/A public int compareTo(Path obj) {
893N/A if (obj == null)
893N/A throw new NullPointerException();
893N/A String s1 = path;
893N/A String s2 = ((WindowsPath)obj).path;
893N/A int n1 = s1.length();
893N/A int n2 = s2.length();
893N/A int min = Math.min(n1, n2);
893N/A for (int i = 0; i < min; i++) {
893N/A char c1 = s1.charAt(i);
893N/A char c2 = s2.charAt(i);
893N/A if (c1 != c2) {
893N/A c1 = Character.toUpperCase(c1);
893N/A c2 = Character.toUpperCase(c2);
893N/A if (c1 != c2) {
893N/A return c1 - c2;
893N/A }
893N/A }
893N/A }
893N/A return n1 - n2;
893N/A }
893N/A
893N/A @Override
893N/A public boolean equals(Object obj) {
893N/A if ((obj != null) && (obj instanceof WindowsPath)) {
893N/A return compareTo((Path)obj) == 0;
893N/A }
893N/A return false;
893N/A }
893N/A
893N/A @Override
893N/A public int hashCode() {
893N/A // OK if two or more threads compute hash
893N/A int h = hash;
893N/A if (h == 0) {
893N/A for (int i = 0; i< path.length(); i++) {
893N/A h = 31*h + Character.toUpperCase(path.charAt(i));
893N/A }
893N/A hash = h;
893N/A }
893N/A return h;
893N/A }
893N/A
893N/A @Override
893N/A public String toString() {
893N/A return path;
893N/A }
893N/A
893N/A // -- file operations --
893N/A
893N/A // package-private
893N/A long openForReadAttributeAccess(boolean followLinks)
893N/A throws WindowsException
893N/A {
893N/A int flags = FILE_FLAG_BACKUP_SEMANTICS;
893N/A if (!followLinks && getFileSystem().supportsLinks())
893N/A flags |= FILE_FLAG_OPEN_REPARSE_POINT;
893N/A return CreateFile(getPathForWin32Calls(),
893N/A FILE_READ_ATTRIBUTES,
893N/A (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
893N/A 0L,
893N/A OPEN_EXISTING,
893N/A flags);
893N/A }
893N/A
893N/A void checkRead() {
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A sm.checkRead(getPathForPermissionCheck());
893N/A }
893N/A }
893N/A
893N/A void checkWrite() {
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A sm.checkWrite(getPathForPermissionCheck());
893N/A }
893N/A }
893N/A
893N/A void checkDelete() {
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A sm.checkDelete(getPathForPermissionCheck());
893N/A }
893N/A }
893N/A
893N/A @Override
893N/A public URI toUri() {
893N/A return WindowsUriSupport.toUri(this);
893N/A }
893N/A
893N/A @Override
893N/A public WindowsPath toAbsolutePath() {
893N/A if (isAbsolute())
893N/A return this;
893N/A
893N/A // permission check as per spec
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A sm.checkPropertyAccess("user.dir");
893N/A }
893N/A
893N/A try {
893N/A return createFromNormalizedPath(getFileSystem(), getAbsolutePath());
893N/A } catch (WindowsException x) {
893N/A throw new IOError(new IOException(x.getMessage()));
893N/A }
893N/A }
893N/A
893N/A @Override
3899N/A public WindowsPath toRealPath(LinkOption... options) throws IOException {
893N/A checkRead();
3899N/A String rp = WindowsLinkSupport.getRealPath(this, Util.followLinks(options));
893N/A return createFromNormalizedPath(getFileSystem(), rp);
893N/A }
893N/A
893N/A @Override
893N/A public WatchKey register(WatchService watcher,
893N/A WatchEvent.Kind<?>[] events,
893N/A WatchEvent.Modifier... modifiers)
893N/A throws IOException
893N/A {
893N/A if (watcher == null)
893N/A throw new NullPointerException();
893N/A if (!(watcher instanceof WindowsWatchService))
893N/A throw new ProviderMismatchException();
893N/A
893N/A // When a security manager is set then we need to make a defensive
893N/A // copy of the modifiers and check for the Windows specific FILE_TREE
893N/A // modifier. When the modifier is present then check that permission
893N/A // has been granted recursively.
893N/A SecurityManager sm = System.getSecurityManager();
893N/A if (sm != null) {
893N/A boolean watchSubtree = false;
893N/A final int ml = modifiers.length;
893N/A if (ml > 0) {
893N/A modifiers = Arrays.copyOf(modifiers, ml);
893N/A int i=0;
893N/A while (i < ml) {
893N/A if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) {
893N/A watchSubtree = true;
893N/A break;
893N/A }
893N/A }
893N/A }
893N/A String s = getPathForPermissionCheck();
893N/A sm.checkRead(s);
893N/A if (watchSubtree)
893N/A sm.checkRead(s + "\\-");
893N/A }
893N/A
893N/A return ((WindowsWatchService)watcher).register(this, events, modifiers);
893N/A }
893N/A}