rev 11154 : 8030090: (fs) Add default methods to Path for derived methods
Summary: Use method bodies from sun.nio.fs.AbstractPath as default method implementations in Path.
Reviewed-by: alanb
1 /*
2 * Copyright (c) 2008, 2013, 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 sun.nio.fs;
27
28 import java.nio.file.*;
29 import java.nio.file.attribute.*;
30 import java.io.*;
31 import java.net.URI;
32 import java.util.*;
33 import java.lang.ref.WeakReference;
34
35 import com.sun.nio.file.ExtendedWatchEventModifier;
36
37 import static sun.nio.fs.WindowsNativeDispatcher.*;
38 import static sun.nio.fs.WindowsConstants.*;
39
40 /**
41 * Windows implementation of Path
42 */
43
44 class WindowsPath implements Path {
45
46 // The maximum path that does not require long path prefix. On Windows
47 // the maximum path is 260 minus 1 (NUL) but for directories it is 260
48 // minus 12 minus 1 (to allow for the creation of a 8.3 file in the
49 // directory).
50 private static final int MAX_PATH = 247;
51
52 // Maximum extended-length path
53 private static final int MAX_LONG_PATH = 32000;
54
55 // FIXME - eliminate this reference to reduce space
56 private final WindowsFileSystem fs;
57
58 // path type
59 private final WindowsPathType type;
60 // root component (may be empty)
61 private final String root;
62 // normalized path
63 private final String path;
64
65 // the path to use in Win32 calls. This differs from path for relative
66 // paths and has a long path prefix for all paths longer than MAX_PATH.
67 private volatile WeakReference<String> pathForWin32Calls;
68
69 // offsets into name components (computed lazily)
70 private volatile Integer[] offsets;
71
72 // computed hash code (computed lazily, no need to be volatile)
73 private int hash;
74
75
76 /**
77 * Initializes a new instance of this class.
78 */
79 private WindowsPath(WindowsFileSystem fs,
80 WindowsPathType type,
81 String root,
82 String path)
83 {
84 this.fs = fs;
85 this.type = type;
86 this.root = root;
87 this.path = path;
88 }
89
90 /**
91 * Creates a Path by parsing the given path.
92 */
93 static WindowsPath parse(WindowsFileSystem fs, String path) {
94 WindowsPathParser.Result result = WindowsPathParser.parse(path);
95 return new WindowsPath(fs, result.type(), result.root(), result.path());
96 }
97
98 /**
99 * Creates a Path from a given path that is known to be normalized.
100 */
101 static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
102 String path,
103 BasicFileAttributes attrs)
104 {
105 try {
106 WindowsPathParser.Result result =
107 WindowsPathParser.parseNormalizedPath(path);
108 if (attrs == null) {
109 return new WindowsPath(fs,
110 result.type(),
111 result.root(),
112 result.path());
113 } else {
114 return new WindowsPathWithAttributes(fs,
115 result.type(),
116 result.root(),
117 result.path(),
118 attrs);
119 }
120 } catch (InvalidPathException x) {
121 throw new AssertionError(x.getMessage());
122 }
123 }
124
125 /**
126 * Creates a WindowsPath from a given path that is known to be normalized.
127 */
128 static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
129 String path)
130 {
131 return createFromNormalizedPath(fs, path, null);
132 }
133
134 /**
135 * Special implementation with attached/cached attributes (used to quicken
136 * file tree traversal)
137 */
138 private static class WindowsPathWithAttributes
139 extends WindowsPath implements BasicFileAttributesHolder
140 {
141 final WeakReference<BasicFileAttributes> ref;
142
143 WindowsPathWithAttributes(WindowsFileSystem fs,
144 WindowsPathType type,
145 String root,
146 String path,
147 BasicFileAttributes attrs)
148 {
149 super(fs, type, root, path);
150 ref = new WeakReference<BasicFileAttributes>(attrs);
151 }
152
153 @Override
154 public BasicFileAttributes get() {
155 return ref.get();
156 }
157
158 @Override
159 public void invalidate() {
160 ref.clear();
161 }
162
163 // no need to override equals/hashCode.
164 }
165
166 // use this message when throwing exceptions
167 String getPathForExceptionMessage() {
168 return path;
169 }
170
171 // use this path for permission checks
172 String getPathForPermissionCheck() {
173 return path;
174 }
175
176 // use this path for Win32 calls
177 // This method will prefix long paths with \\?\ or \\?\UNC as required.
178 String getPathForWin32Calls() throws WindowsException {
179 // short absolute paths can be used directly
180 if (isAbsolute() && path.length() <= MAX_PATH)
181 return path;
182
183 // return cached values if available
184 WeakReference<String> ref = pathForWin32Calls;
185 String resolved = (ref != null) ? ref.get() : null;
186 if (resolved != null) {
187 // Win32 path already available
188 return resolved;
189 }
190
191 // resolve against default directory
192 resolved = getAbsolutePath();
193
194 // Long paths need to have "." and ".." removed and be prefixed with
195 // "\\?\". Note that it is okay to remove ".." even when it follows
196 // a link - for example, it is okay for foo/link/../bar to be changed
197 // to foo/bar. The reason is that Win32 APIs to access foo/link/../bar
198 // will access foo/bar anyway (which differs to Unix systems)
199 if (resolved.length() > MAX_PATH) {
200 if (resolved.length() > MAX_LONG_PATH) {
201 throw new WindowsException("Cannot access file with path exceeding "
202 + MAX_LONG_PATH + " characters");
203 }
204 resolved = addPrefixIfNeeded(GetFullPathName(resolved));
205 }
206
207 // cache the resolved path (except drive relative paths as the working
208 // directory on removal media devices can change during the lifetime
209 // of the VM)
210 if (type != WindowsPathType.DRIVE_RELATIVE) {
211 synchronized (path) {
212 pathForWin32Calls = new WeakReference<String>(resolved);
213 }
214 }
215 return resolved;
216 }
217
218 // return this path resolved against the file system's default directory
219 private String getAbsolutePath() throws WindowsException {
220 if (isAbsolute())
221 return path;
222
223 // Relative path ("foo" for example)
224 if (type == WindowsPathType.RELATIVE) {
225 String defaultDirectory = getFileSystem().defaultDirectory();
226 if (isEmpty())
227 return defaultDirectory;
228 if (defaultDirectory.endsWith("\\")) {
229 return defaultDirectory + path;
230 } else {
231 StringBuilder sb =
232 new StringBuilder(defaultDirectory.length() + path.length() + 1);
233 return sb.append(defaultDirectory).append('\\').append(path).toString();
234 }
235 }
236
237 // Directory relative path ("\foo" for example)
238 if (type == WindowsPathType.DIRECTORY_RELATIVE) {
239 String defaultRoot = getFileSystem().defaultRoot();
240 return defaultRoot + path.substring(1);
241 }
242
243 // Drive relative path ("C:foo" for example).
244 if (isSameDrive(root, getFileSystem().defaultRoot())) {
245 // relative to default directory
246 String remaining = path.substring(root.length());
247 String defaultDirectory = getFileSystem().defaultDirectory();
248 String result;
249 if (defaultDirectory.endsWith("\\")) {
250 result = defaultDirectory + remaining;
251 } else {
252 result = defaultDirectory + "\\" + remaining;
253 }
254 return result;
255 } else {
256 // relative to some other drive
257 String wd;
258 try {
259 int dt = GetDriveType(root + "\\");
260 if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR)
261 throw new WindowsException("");
262 wd = GetFullPathName(root + ".");
263 } catch (WindowsException x) {
264 throw new WindowsException("Unable to get working directory of drive '" +
265 Character.toUpperCase(root.charAt(0)) + "'");
266 }
267 String result = wd;
268 if (wd.endsWith("\\")) {
269 result += path.substring(root.length());
270 } else {
271 if (path.length() > root.length())
272 result += "\\" + path.substring(root.length());
273 }
274 return result;
275 }
276 }
277
278 // returns true if same drive letter
279 private static boolean isSameDrive(String root1, String root2) {
280 return Character.toUpperCase(root1.charAt(0)) ==
281 Character.toUpperCase(root2.charAt(0));
282 }
283
284 // Add long path prefix to path if required
285 static String addPrefixIfNeeded(String path) {
286 if (path.length() > MAX_PATH) {
287 if (path.startsWith("\\\\")) {
288 path = "\\\\?\\UNC" + path.substring(1, path.length());
289 } else {
290 path = "\\\\?\\" + path;
291 }
292 }
293 return path;
294 }
295
296 @Override
297 public WindowsFileSystem getFileSystem() {
298 return fs;
299 }
300
301 // -- Path operations --
302
303 private boolean isEmpty() {
304 return path.length() == 0;
305 }
306
307 private WindowsPath emptyPath() {
308 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", "");
309 }
310
311 @Override
312 public Path getFileName() {
313 int len = path.length();
314 // represents empty path
315 if (len == 0)
316 return this;
317 // represents root component only
318 if (root.length() == len)
319 return null;
320 int off = path.lastIndexOf('\\');
321 if (off < root.length())
322 off = root.length();
323 else
324 off++;
325 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off));
326 }
327
328 @Override
329 public WindowsPath getParent() {
330 // represents root component only
331 if (root.length() == path.length())
332 return null;
333 int off = path.lastIndexOf('\\');
334 if (off < root.length())
335 return getRoot();
336 else
337 return new WindowsPath(getFileSystem(),
338 type,
339 root,
340 path.substring(0, off));
341 }
342
343 @Override
344 public WindowsPath getRoot() {
345 if (root.length() == 0)
346 return null;
347 return new WindowsPath(getFileSystem(), type, root, root);
348 }
349
350 // package-private
351 WindowsPathType type() {
352 return type;
353 }
354
355 // package-private
356 boolean isUnc() {
357 return type == WindowsPathType.UNC;
358 }
359
360 boolean needsSlashWhenResolving() {
361 if (path.endsWith("\\"))
362 return false;
363 return path.length() > root.length();
364 }
365
366 @Override
367 public boolean isAbsolute() {
368 return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
369 }
370
371 static WindowsPath toWindowsPath(Path path) {
372 if (path == null)
373 throw new NullPointerException();
374 if (!(path instanceof WindowsPath)) {
375 throw new ProviderMismatchException();
376 }
377 return (WindowsPath)path;
378 }
379
380 @Override
381 public WindowsPath relativize(Path obj) {
382 WindowsPath other = toWindowsPath(obj);
383 if (this.equals(other))
384 return emptyPath();
385
386 // can only relativize paths of the same type
387 if (this.type != other.type)
388 throw new IllegalArgumentException("'other' is different type of Path");
389
390 // can only relativize paths if root component matches
391 if (!this.root.equalsIgnoreCase(other.root))
392 throw new IllegalArgumentException("'other' has different root");
393
394 int bn = this.getNameCount();
395 int cn = other.getNameCount();
396
397 // skip matching names
398 int n = (bn > cn) ? cn : bn;
399 int i = 0;
400 while (i < n) {
401 if (!this.getName(i).equals(other.getName(i)))
402 break;
403 i++;
404 }
405
406 // append ..\ for remaining names in the base
407 StringBuilder result = new StringBuilder();
408 for (int j=i; j<bn; j++) {
409 result.append("..\\");
410 }
411
412 // append remaining names in child
413 for (int j=i; j<cn; j++) {
414 result.append(other.getName(j).toString());
415 result.append("\\");
416 }
417
418 // drop trailing slash in result
419 result.setLength(result.length()-1);
420 return createFromNormalizedPath(getFileSystem(), result.toString());
421 }
422
423 @Override
424 public Path normalize() {
425 final int count = getNameCount();
426 if (count == 0 || isEmpty())
427 return this;
428
429 boolean[] ignore = new boolean[count]; // true => ignore name
430 int remaining = count; // number of names remaining
431
432 // multiple passes to eliminate all occurrences of "." and "name/.."
433 int prevRemaining;
434 do {
435 prevRemaining = remaining;
436 int prevName = -1;
437 for (int i=0; i<count; i++) {
438 if (ignore[i])
439 continue;
440
441 String name = elementAsString(i);
442
443 // not "." or ".."
444 if (name.length() > 2) {
445 prevName = i;
446 continue;
447 }
448
449 // "." or something else
450 if (name.length() == 1) {
451 // ignore "."
452 if (name.charAt(0) == '.') {
453 ignore[i] = true;
454 remaining--;
455 } else {
456 prevName = i;
457 }
458 continue;
459 }
460
461 // not ".."
462 if (name.charAt(0) != '.' || name.charAt(1) != '.') {
463 prevName = i;
464 continue;
465 }
466
467 // ".." found
468 if (prevName >= 0) {
469 // name/<ignored>/.. found so mark name and ".." to be
470 // ignored
471 ignore[prevName] = true;
472 ignore[i] = true;
473 remaining = remaining - 2;
474 prevName = -1;
475 } else {
476 // Cases:
477 // C:\<ignored>\..
478 // \\server\\share\<ignored>\..
479 // \<ignored>..
480 if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) {
481 boolean hasPrevious = false;
482 for (int j=0; j<i; j++) {
483 if (!ignore[j]) {
484 hasPrevious = true;
485 break;
486 }
487 }
488 if (!hasPrevious) {
489 // all proceeding names are ignored
490 ignore[i] = true;
491 remaining--;
492 }
493 }
494 }
495 }
496 } while (prevRemaining > remaining);
497
498 // no redundant names
499 if (remaining == count)
500 return this;
501
502 // corner case - all names removed
503 if (remaining == 0) {
504 return (root.length() == 0) ? emptyPath() : getRoot();
505 }
506
507 // re-constitute the path from the remaining names.
508 StringBuilder result = new StringBuilder();
509 if (root != null)
510 result.append(root);
511 for (int i=0; i<count; i++) {
512 if (!ignore[i]) {
513 result.append(getName(i));
514 result.append("\\");
515 }
516 }
517
518 // drop trailing slash in result
519 result.setLength(result.length()-1);
520 return createFromNormalizedPath(getFileSystem(), result.toString());
521 }
522
523 @Override
524 public WindowsPath resolve(Path obj) {
525 WindowsPath other = toWindowsPath(obj);
526 if (other.isEmpty())
527 return this;
528 if (other.isAbsolute())
529 return other;
530
531 switch (other.type) {
532 case RELATIVE: {
533 String result;
534 if (path.endsWith("\\") || (root.length() == path.length())) {
535 result = path + other.path;
536 } else {
537 result = path + "\\" + other.path;
538 }
539 return new WindowsPath(getFileSystem(), type, root, result);
540 }
541
542 case DIRECTORY_RELATIVE: {
543 String result;
544 if (root.endsWith("\\")) {
545 result = root + other.path.substring(1);
546 } else {
547 result = root + other.path;
548 }
549 return createFromNormalizedPath(getFileSystem(), result);
550 }
551
552 case DRIVE_RELATIVE: {
553 if (!root.endsWith("\\"))
554 return other;
555 // if different roots then return other
556 String thisRoot = root.substring(0, root.length()-1);
557 if (!thisRoot.equalsIgnoreCase(other.root))
558 return other;
559 // same roots
560 String remaining = other.path.substring(other.root.length());
561 String result;
562 if (path.endsWith("\\")) {
563 result = path + remaining;
564 } else {
565 result = path + "\\" + remaining;
566 }
567 return createFromNormalizedPath(getFileSystem(), result);
568 }
569
570 default:
571 throw new AssertionError();
572 }
573 }
574
575 // generate offset array
576 private void initOffsets() {
577 if (offsets == null) {
578 ArrayList<Integer> list = new ArrayList<>();
579 if (isEmpty()) {
580 // empty path considered to have one name element
581 list.add(0);
582 } else {
583 int start = root.length();
584 int off = root.length();
585 while (off < path.length()) {
586 if (path.charAt(off) != '\\') {
587 off++;
588 } else {
589 list.add(start);
590 start = ++off;
591 }
592 }
593 if (start != off)
594 list.add(start);
595 }
596 synchronized (this) {
597 if (offsets == null)
598 offsets = list.toArray(new Integer[list.size()]);
599 }
600 }
601 }
602
603 @Override
604 public int getNameCount() {
605 initOffsets();
606 return offsets.length;
607 }
608
609 private String elementAsString(int i) {
610 initOffsets();
611 if (i == (offsets.length-1))
612 return path.substring(offsets[i]);
613 return path.substring(offsets[i], offsets[i+1]-1);
614 }
615
616 @Override
617 public WindowsPath getName(int index) {
618 initOffsets();
619 if (index < 0 || index >= offsets.length)
620 throw new IllegalArgumentException();
621 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index));
622 }
623
624 @Override
625 public WindowsPath subpath(int beginIndex, int endIndex) {
626 initOffsets();
627 if (beginIndex < 0)
628 throw new IllegalArgumentException();
629 if (beginIndex >= offsets.length)
630 throw new IllegalArgumentException();
631 if (endIndex > offsets.length)
632 throw new IllegalArgumentException();
633 if (beginIndex >= endIndex)
634 throw new IllegalArgumentException();
635
636 StringBuilder sb = new StringBuilder();
637 Integer[] nelems = new Integer[endIndex - beginIndex];
638 for (int i = beginIndex; i < endIndex; i++) {
639 nelems[i-beginIndex] = sb.length();
640 sb.append(elementAsString(i));
641 if (i != (endIndex-1))
642 sb.append("\\");
643 }
644 return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString());
645 }
646
647 @Override
648 public boolean startsWith(Path obj) {
649 if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
650 return false;
651 WindowsPath other = (WindowsPath)obj;
652
653 // if this path has a root component the given path's root must match
654 if (!this.root.equalsIgnoreCase(other.root)) {
655 return false;
656 }
657
658 // empty path starts with itself
659 if (other.isEmpty())
660 return this.isEmpty();
661
662 // roots match so compare elements
663 int thisCount = getNameCount();
664 int otherCount = other.getNameCount();
665 if (otherCount <= thisCount) {
666 while (--otherCount >= 0) {
667 String thisElement = this.elementAsString(otherCount);
668 String otherElement = other.elementAsString(otherCount);
669 // FIXME: should compare in uppercase
670 if (!thisElement.equalsIgnoreCase(otherElement))
671 return false;
672 }
673 return true;
674 }
675 return false;
676 }
677
678 @Override
679 public boolean endsWith(Path obj) {
680 if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
681 return false;
682 WindowsPath other = (WindowsPath)obj;
683
684 // other path is longer
685 if (other.path.length() > this.path.length()) {
686 return false;
687 }
688
689 // empty path ends in itself
690 if (other.isEmpty()) {
691 return this.isEmpty();
692 }
693
694 int thisCount = this.getNameCount();
695 int otherCount = other.getNameCount();
696
697 // given path has more elements that this path
698 if (otherCount > thisCount) {
699 return false;
700 }
701
702 // compare roots
703 if (other.root.length() > 0) {
704 if (otherCount < thisCount)
705 return false;
706 // FIXME: should compare in uppercase
707 if (!this.root.equalsIgnoreCase(other.root))
708 return false;
709 }
710
711 // match last 'otherCount' elements
712 int off = thisCount - otherCount;
713 while (--otherCount >= 0) {
714 String thisElement = this.elementAsString(off + otherCount);
715 String otherElement = other.elementAsString(otherCount);
716 // FIXME: should compare in uppercase
717 if (!thisElement.equalsIgnoreCase(otherElement))
718 return false;
719 }
720 return true;
721 }
722
723 @Override
724 public int compareTo(Path obj) {
725 if (obj == null)
726 throw new NullPointerException();
727 String s1 = path;
728 String s2 = ((WindowsPath)obj).path;
729 int n1 = s1.length();
730 int n2 = s2.length();
731 int min = Math.min(n1, n2);
732 for (int i = 0; i < min; i++) {
733 char c1 = s1.charAt(i);
734 char c2 = s2.charAt(i);
735 if (c1 != c2) {
736 c1 = Character.toUpperCase(c1);
737 c2 = Character.toUpperCase(c2);
738 if (c1 != c2) {
739 return c1 - c2;
740 }
741 }
742 }
743 return n1 - n2;
744 }
745
746 @Override
747 public boolean equals(Object obj) {
748 if ((obj != null) && (obj instanceof WindowsPath)) {
749 return compareTo((Path)obj) == 0;
750 }
751 return false;
752 }
753
754 @Override
755 public int hashCode() {
756 // OK if two or more threads compute hash
757 int h = hash;
758 if (h == 0) {
759 for (int i = 0; i< path.length(); i++) {
760 h = 31*h + Character.toUpperCase(path.charAt(i));
761 }
762 hash = h;
763 }
764 return h;
765 }
766
767 @Override
768 public String toString() {
769 return path;
770 }
771
772 // -- file operations --
773
774 // package-private
775 long openForReadAttributeAccess(boolean followLinks)
776 throws WindowsException
777 {
778 int flags = FILE_FLAG_BACKUP_SEMANTICS;
779 if (!followLinks && getFileSystem().supportsLinks())
780 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
781 return CreateFile(getPathForWin32Calls(),
782 FILE_READ_ATTRIBUTES,
783 (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
784 0L,
785 OPEN_EXISTING,
786 flags);
787 }
788
789 void checkRead() {
790 SecurityManager sm = System.getSecurityManager();
791 if (sm != null) {
792 sm.checkRead(getPathForPermissionCheck());
793 }
794 }
795
796 void checkWrite() {
797 SecurityManager sm = System.getSecurityManager();
798 if (sm != null) {
799 sm.checkWrite(getPathForPermissionCheck());
800 }
801 }
802
803 void checkDelete() {
804 SecurityManager sm = System.getSecurityManager();
805 if (sm != null) {
806 sm.checkDelete(getPathForPermissionCheck());
807 }
808 }
809
810 @Override
811 public URI toUri() {
812 return WindowsUriSupport.toUri(this);
813 }
814
815 @Override
816 public WindowsPath toAbsolutePath() {
817 if (isAbsolute())
818 return this;
819
820 // permission check as per spec
821 SecurityManager sm = System.getSecurityManager();
822 if (sm != null) {
823 sm.checkPropertyAccess("user.dir");
824 }
825
826 try {
827 return createFromNormalizedPath(getFileSystem(), getAbsolutePath());
828 } catch (WindowsException x) {
829 throw new IOError(new IOException(x.getMessage()));
830 }
831 }
832
833 @Override
834 public WindowsPath toRealPath(LinkOption... options) throws IOException {
835 checkRead();
836 String rp = WindowsLinkSupport.getRealPath(this, Util.followLinks(options));
837 return createFromNormalizedPath(getFileSystem(), rp);
838 }
839
840 @Override
841 public WatchKey register(WatchService watcher,
842 WatchEvent.Kind<?>[] events,
843 WatchEvent.Modifier... modifiers)
844 throws IOException
845 {
846 if (watcher == null)
847 throw new NullPointerException();
848 if (!(watcher instanceof WindowsWatchService))
849 throw new ProviderMismatchException();
850
851 // When a security manager is set then we need to make a defensive
852 // copy of the modifiers and check for the Windows specific FILE_TREE
853 // modifier. When the modifier is present then check that permission
854 // has been granted recursively.
855 SecurityManager sm = System.getSecurityManager();
856 if (sm != null) {
857 boolean watchSubtree = false;
858 final int ml = modifiers.length;
859 if (ml > 0) {
860 modifiers = Arrays.copyOf(modifiers, ml);
861 int i=0;
862 while (i < ml) {
863 if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) {
864 watchSubtree = true;
865 break;
866 }
867 }
868 }
869 String s = getPathForPermissionCheck();
870 sm.checkRead(s);
871 if (watchSubtree)
872 sm.checkRead(s + "\\-");
873 }
874
875 return ((WindowsWatchService)watcher).register(this, events, modifiers);
876 }
877 }
--- EOF ---