changeset 3940:8d53286f1fe7

7017446: (fs) Updates to file system API (3/2011) Reviewed-by: sherman
author alanb
date Sat, 19 Mar 2011 14:21:52 +0000
parents 0fdc4553f79f
children be5a4198c468
files src/share/classes/java/nio/file/Files.java src/share/classes/java/nio/file/Path.java src/share/classes/java/nio/file/WatchKey.java src/share/classes/java/nio/file/attribute/FileTime.java src/share/classes/java/nio/file/spi/FileSystemProvider.java src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java src/share/classes/sun/nio/fs/AbstractFileSystemProvider.java src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java src/share/classes/sun/nio/fs/AbstractWatchKey.java src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java src/share/classes/sun/nio/fs/Util.java src/share/sample/nio/file/WatchDir.java src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java src/solaris/classes/sun/nio/fs/UnixPath.java src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java src/windows/classes/sun/nio/fs/WindowsPath.java test/java/nio/file/Files/FileAttributes.java test/java/nio/file/WatchService/Basic.java test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java
diffstat 21 files changed, 248 insertions(+), 126 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/nio/file/Files.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/java/nio/file/Files.java	Sat Mar 19 14:21:52 2011 +0000
@@ -1712,10 +1712,10 @@
      * @return  the {@code path} parameter
      *
      * @throws  UnsupportedOperationException
-     *          if the attribute view is not available or it does not support
-     *          updating the attribute
+     *          if the attribute view is not available
      * @throws  IllegalArgumentException
-     *          if the attribute value is of the correct type but has an
+     *          if the attribute name is not specified, or is not recognized, or
+     *          the attribute value is of the correct type but has an
      *          inappropriate value
      * @throws  ClassCastException
      *          if the attribute value is not of the expected type or is a
@@ -1776,9 +1776,12 @@
      * @param   options
      *          options indicating how symbolic links are handled
      *
-     * @return  the attribute value or {@code null} if the attribute view
-     *          is not available or it does not support reading the attribute
+     * @return  the attribute value
      *
+     * @throws  UnsupportedOperationException
+     *          if the attribute view is not available
+     * @throws  IllegalArgumentException
+     *          if the attribute name is not specified or is not recognized
      * @throws  IOException
      *          if an I/O error occurs
      * @throws  SecurityException
@@ -1794,8 +1797,9 @@
     {
         // only one attribute should be read
         if (attribute.indexOf('*') >= 0 || attribute.indexOf(',') >= 0)
-            return null;
+            throw new IllegalArgumentException(attribute);
         Map<String,Object> map = readAttributes(path, attribute, options);
+        assert map.size() == 1;
         String name;
         int pos = attribute.indexOf(':');
         if (pos == -1) {
@@ -1868,9 +1872,14 @@
      * @param   options
      *          options indicating how symbolic links are handled
      *
-     * @return  a map of the attributes returned; may be empty. The map's keys
-     *          are the attribute names, its values are the attribute values
+     * @return  a map of the attributes returned; The map's keys are the
+     *          attribute names, its values are the attribute values
      *
+     * @throws  UnsupportedOperationException
+     *          if the attribute view is not available
+     * @throws  IllegalArgumentException
+     *          if no attributes are specified or an unrecognized attributes is
+     *          specified
      * @throws  IOException
      *          if an I/O error occurs
      * @throws  SecurityException
--- a/src/share/classes/java/nio/file/Path.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/java/nio/file/Path.java	Sat Mar 19 14:21:52 2011 +0000
@@ -228,6 +228,9 @@
      * not have a root component and the given path has a root component then
      * this path does not start with the given path.
      *
+     * <p> If the given path is associated with a different {@code FileSystem}
+     * to this path then {@code false} is returned.
+     *
      * @param   other
      *          the given path
      *
@@ -270,6 +273,9 @@
      * does not have a root component and the given path has a root component
      * then this path does not end with the given path.
      *
+     * <p> If the given path is associated with a different {@code FileSystem}
+     * to this path then {@code false} is returned.
+     *
      * @param   other
      *          the given path
      *
@@ -283,7 +289,10 @@
      * the given path string, in exactly the manner specified by the {@link
      * #endsWith(Path) endsWith(Path)} method. On UNIX for example, the path
      * "{@code foo/bar}" ends with "{@code foo/bar}" and "{@code bar}". It does
-     * not end with "{@code r}" or "{@code /bar}".
+     * not end with "{@code r}" or "{@code /bar}". Note that trailing separators
+     * are not taken into account, and so invoking this method on the {@code
+     * Path}"{@code foo/bar}" with the {@code String} "{@code bar/}" returns
+     * {@code true}.
      *
      * @param   other
      *          the given path string
@@ -724,12 +733,18 @@
      * provider, platform specific. This method does not access the file system
      * and neither file is required to exist.
      *
+     * <p> This method may not be used to compare paths that are associated
+     * with different file system providers.
+     *
      * @param   other  the path compared to this path.
      *
      * @return  zero if the argument is {@link #equals equal} to this path, a
      *          value less than zero if this path is lexicographically less than
      *          the argument, or a value greater than zero if this path is
      *          lexicographically greater than the argument
+     *
+     * @throws  ClassCastException
+     *          if the paths are associated with different providers
      */
     @Override
     int compareTo(Path other);
@@ -738,7 +753,7 @@
      * Tests this path for equality with the given object.
      *
      * <p> If the given object is not a Path, or is a Path associated with a
-     * different provider, then this method immediately returns {@code false}.
+     * different {@code FileSystem}, then this method returns {@code false}.
      *
      * <p> Whether or not two path are equal depends on the file system
      * implementation. In some cases the paths are compared without regard
--- a/src/share/classes/java/nio/file/WatchKey.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/java/nio/file/WatchKey.java	Sat Mar 19 14:21:52 2011 +0000
@@ -146,5 +146,5 @@
      *
      * @return the object for which this watch key was created
      */
-    //T watchable();
+    Watchable watchable();
 }
--- a/src/share/classes/java/nio/file/attribute/FileTime.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/java/nio/file/attribute/FileTime.java	Sat Mar 19 14:21:52 2011 +0000
@@ -216,12 +216,14 @@
      * "2009-02-13T23:31:30Z"}, and {@code FileTime.fromMillis(1234567890123L).toString()}
      * yields {@code "2009-02-13T23:31:30.123Z"}.
      *
-     * <p> A {@code FileTime} is primarly intended to represent the value of a
+     * <p> A {@code FileTime} is primarily intended to represent the value of a
      * file's time stamp. Where used to represent <i>extreme values</i>, where
      * the year is less than "{@code 0001}" or greater than "{@code 9999}" then
-     * the year may be expanded to more than four digits and may be
-     * negative-signed. If more than four digits then leading zeros are not
-     * present. The year before "{@code 0001}" is "{@code -0001}".
+     * this method deviates from ISO 8601 in the same manner as the
+     * <a href="http://www.w3.org/TR/xmlschema-2/#deviantformats">XML Schema
+     * language</a>. That is, the year may be expanded to more than four digits
+     * and may be negative-signed. If more than four digits then leading zeros
+     * are not present. The year before "{@code 0001}" is "{@code -0001}".
      *
      * @return  the string representation of this file time
      */
--- a/src/share/classes/java/nio/file/spi/FileSystemProvider.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/java/nio/file/spi/FileSystemProvider.java	Sat Mar 19 14:21:52 2011 +0000
@@ -1037,6 +1037,11 @@
      * @return  a map of the attributes returned; may be empty. The map's keys
      *          are the attribute names, its values are the attribute values
      *
+     * @throws  UnsupportedOperationException
+     *          if the attribute view is not available
+     * @throws  IllegalArgumentException
+     *          if no attributes are specified or an unrecognized attributes is
+     *          specified
      * @throws  IOException
      *          If an I/O error occurs
      * @throws  SecurityException
@@ -1064,10 +1069,10 @@
      *          options indicating how symbolic links are handled
      *
      * @throws  UnsupportedOperationException
-     *          if the attribute view is not available or it does not support
-     *          updating the attribute
+     *          if the attribute view is not available
      * @throws  IllegalArgumentException
-     *          if the attribute value is of the correct type but has an
+     *          if the attribute name is not specified, or is not recognized, or
+     *          the attribute value is of the correct type but has an
      *          inappropriate value
      * @throws  ClassCastException
      *          If the attribute value is not of the expected type or is a
--- a/src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java	Sat Mar 19 14:21:52 2011 +0000
@@ -57,8 +57,8 @@
             setAcl((List<AclEntry>)value);
             return;
         }
-        throw new UnsupportedOperationException("'" + name() + ":" +
-                attribute + "' not supported");
+        throw new IllegalArgumentException("'" + name() + ":" +
+            attribute + "' not recognized");
     }
 
     @Override
@@ -81,6 +81,8 @@
                 owner = true;
                 continue;
             }
+            throw new IllegalArgumentException("'" + name() + ":" +
+                attribute + "' not recognized");
         }
         Map<String,Object> result = new HashMap<>(2);
         if (acl)
--- a/src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java	Sat Mar 19 14:21:52 2011 +0000
@@ -46,6 +46,18 @@
     private static final String IS_SYMBOLIC_LINK_NAME = "isSymbolicLink";
     private static final String IS_OTHER_NAME = "isOther";
 
+    // the names of the basic attributes
+    static final Set<String> basicAttributeNames =
+        Util.newSet(SIZE_NAME,
+                    CREATION_TIME_NAME,
+                    LAST_ACCESS_TIME_NAME,
+                    LAST_MODIFIED_TIME_NAME,
+                    FILE_KEY_NAME,
+                    IS_DIRECTORY_NAME,
+                    IS_REGULAR_FILE_NAME,
+                    IS_SYMBOLIC_LINK_NAME,
+                    IS_OTHER_NAME);
+
     protected AbstractBasicFileAttributeView() { }
 
     @Override
@@ -69,24 +81,26 @@
             setTimes(null, null, (FileTime)value);
             return;
         }
-        throw new UnsupportedOperationException("'" + attribute +
-            "' is unknown or read-only attribute");
+        throw new IllegalArgumentException("'" + name() + ":" +
+            attribute + "' not recognized");
     }
 
     /**
      * Used to build a map of attribute name/values.
      */
     static class AttributesBuilder {
-        private Set<String> set = new HashSet<>();
+        private Set<String> names = new HashSet<>();
         private Map<String,Object> map = new HashMap<>();
         private boolean copyAll;
 
-        private AttributesBuilder(String[] attributes) {
-            for (String attribute: attributes) {
-                if (attribute.equals("*")) {
+        private AttributesBuilder(Set<String> allowed, String[] requested) {
+            for (String name: requested) {
+                if (name.equals("*")) {
                     copyAll = true;
                 } else {
-                    set.add(attribute);
+                    if (!allowed.contains(name))
+                        throw new IllegalArgumentException("'" + name + "' not recognized");
+                    names.add(name);
                 }
             }
         }
@@ -94,21 +108,19 @@
         /**
          * Creates builder to build up a map of the matching attributes
          */
-        static AttributesBuilder create(String[] attributes) {
-            return new AttributesBuilder(attributes);
+        static AttributesBuilder create(Set<String> allowed, String[] requested) {
+            return new AttributesBuilder(allowed, requested);
         }
 
         /**
          * Returns true if the attribute should be returned in the map
          */
-        boolean match(String attribute) {
-            if (copyAll)
-                return true;
-            return set.contains(attribute);
+        boolean match(String name) {
+            return copyAll || names.contains(name);
         }
 
-        void add(String attribute, Object value) {
-            map.put(attribute, value);
+        void add(String name, Object value) {
+            map.put(name, value);
         }
 
         /**
@@ -124,7 +136,7 @@
      * Invoked by readAttributes or sub-classes to add all matching basic
      * attributes to the builder
      */
-    final void addBasicAttributesToBuilder(BasicFileAttributes attrs,
+    final void addRequestedBasicAttributes(BasicFileAttributes attrs,
                                            AttributesBuilder builder)
     {
         if (builder.match(SIZE_NAME))
@@ -148,9 +160,12 @@
     }
 
     @Override
-    public Map<String,Object> readAttributes(String[] attributes) throws IOException {
-        AttributesBuilder builder = AttributesBuilder.create(attributes);
-        addBasicAttributesToBuilder(readAttributes(), builder);
+    public Map<String,Object> readAttributes(String[] requested)
+        throws IOException
+    {
+        AttributesBuilder builder =
+            AttributesBuilder.create(basicAttributeNames, requested);
+        addRequestedBasicAttributes(readAttributes(), builder);
         return builder.unmodifiableMap();
     }
 }
--- a/src/share/classes/sun/nio/fs/AbstractFileSystemProvider.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/AbstractFileSystemProvider.java	Sat Mar 19 14:21:52 2011 +0000
@@ -29,7 +29,6 @@
 import java.nio.file.spi.FileSystemProvider;
 import java.io.IOException;
 import java.util.Map;
-import java.util.Collections;
 
 /**
  * Base implementation class of FileSystemProvider
@@ -72,6 +71,8 @@
         throws IOException
     {
         String[] s = split(attribute);
+        if (s[0].length() == 0)
+            throw new IllegalArgumentException(attribute);
         DynamicFileAttributeView view = getFileAttributeView(file, s[0], options);
         if (view == null)
             throw new UnsupportedOperationException("View '" + s[0] + "' not available");
@@ -83,9 +84,11 @@
         throws IOException
     {
         String[] s = split(attributes);
+        if (s[0].length() == 0)
+            throw new IllegalArgumentException(attributes);
         DynamicFileAttributeView view = getFileAttributeView(file, s[0], options);
         if (view == null)
-            return Collections.emptyMap();
+            throw new UnsupportedOperationException("View '" + s[0] + "' not available");
         return view.readAttributes(s[1].split(","));
     }
 
--- a/src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java	Sat Mar 19 14:21:52 2011 +0000
@@ -59,22 +59,6 @@
         return "user";
     }
 
-    private Object getAttribute(String attribute) throws IOException {
-        int size;
-        try {
-            size = size(attribute);
-        } catch (IOException e) {
-            // not found or some other I/O error
-            if (list().contains(attribute))
-                throw e;
-            return null;
-        }
-
-        byte[] buf = new byte[size];
-        int n = read(attribute, ByteBuffer.wrap(buf));
-        return (n == size) ? buf : Arrays.copyOf(buf, n);
-    }
-
     @Override
     public final void setAttribute(String attribute, Object value)
         throws IOException
@@ -94,12 +78,13 @@
     {
         // names of attributes to return
         List<String> names = new ArrayList<>();
-
         for (String name: attributes) {
             if (name.equals("*")) {
                 names = list();
                 break;
             } else {
+                if (name.length() == 0)
+                    throw new IllegalArgumentException();
                 names.add(name);
             }
         }
@@ -107,11 +92,12 @@
         // read each value and return in map
         Map<String,Object> result = new HashMap<>();
         for (String name: names) {
-            Object value = getAttribute(name);
-            if (value != null)
-                result.put(name, value);
+            int size = size(name);
+            byte[] buf = new byte[size];
+            int n = read(name, ByteBuffer.wrap(buf));
+            byte[] value = (n == size) ? buf : Arrays.copyOf(buf, n);
+            result.put(name, value);
         }
-
         return result;
     }
 }
--- a/src/share/classes/sun/nio/fs/AbstractWatchKey.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/AbstractWatchKey.java	Sat Mar 19 14:21:52 2011 +0000
@@ -81,7 +81,8 @@
     /**
      * Return the original watchable (Path)
      */
-    Path watchable() {
+    @Override
+    public Path watchable() {
         return dir;
     }
 
--- a/src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java	Sat Mar 19 14:21:52 2011 +0000
@@ -63,10 +63,10 @@
     {
         if (attribute.equals(OWNER_NAME)) {
             setOwner((UserPrincipal)value);
-            return;
+        } else {
+            throw new IllegalArgumentException("'" + name() + ":" +
+                attribute + "' not recognized");
         }
-        throw new UnsupportedOperationException("'" + name() + ":" +
-                attribute + "' not supported");
     }
 
     @Override
@@ -75,6 +75,9 @@
         for (String attribute: attributes) {
             if (attribute.equals("*") || attribute.equals(OWNER_NAME)) {
                 result.put(OWNER_NAME, getOwner());
+            } else {
+                throw new IllegalArgumentException("'" + name() + ":" +
+                    attribute + "' not recognized");
             }
         }
         return result;
--- a/src/share/classes/sun/nio/fs/Util.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/classes/sun/nio/fs/Util.java	Sat Mar 19 14:21:52 2011 +0000
@@ -25,6 +25,8 @@
 
 package sun.nio.fs;
 
+import java.util.*;
+
 /**
  * Utility methods
  */
@@ -54,6 +56,28 @@
         }
         result[n] = s.substring(last, s.length());
         return result;
+    }
 
+    /**
+     * Returns a Set containing the given elements.
+     */
+    static <E> Set<E> newSet(E... elements) {
+        HashSet<E> set = new HashSet<>();
+        for (E e: elements) {
+            set.add(e);
+        }
+        return set;
+    }
+
+    /**
+     * Returns a Set containing all the elements of the given Set plus
+     * the given elements.
+     */
+    static <E> Set<E> newSet(Set<E> other, E... elements) {
+        HashSet<E> set = new HashSet<>(other);
+        for (E e: elements) {
+            set.add(e);
+        }
+        return set;
     }
 }
--- a/src/share/sample/nio/file/WatchDir.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/share/sample/nio/file/WatchDir.java	Sat Mar 19 14:21:52 2011 +0000
@@ -33,8 +33,7 @@
 import static java.nio.file.StandardWatchEventKind.*;
 import static java.nio.file.LinkOption.*;
 import java.nio.file.attribute.*;
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
 
 /**
  * Example to watch a directory (or tree) for changes to files.
@@ -43,9 +42,9 @@
 public class WatchDir {
 
     private final WatchService watcher;
-    private final Map<WatchKey,Path> keys;
     private final boolean recursive;
     private boolean trace = false;
+    private int count;
 
     @SuppressWarnings("unchecked")
     static <T> WatchEvent<T> cast(WatchEvent<?> event) {
@@ -57,17 +56,9 @@
      */
     private void register(Path dir) throws IOException {
         WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
-        if (trace) {
-            Path prev = keys.get(key);
-            if (prev == null) {
-                System.out.format("register: %s\n", dir);
-            } else {
-                if (!dir.equals(prev)) {
-                    System.out.format("update: %s -> %s\n", prev, dir);
-                }
-            }
-        }
-        keys.put(key, dir);
+        count++;
+        if (trace)
+            System.out.format("register: %s\n", dir);
     }
 
     /**
@@ -92,7 +83,6 @@
      */
     WatchDir(Path dir, boolean recursive) throws IOException {
         this.watcher = FileSystems.getDefault().newWatchService();
-        this.keys = new HashMap<WatchKey,Path>();
         this.recursive = recursive;
 
         if (recursive) {
@@ -121,12 +111,6 @@
                 return;
             }
 
-            Path dir = keys.get(key);
-            if (dir == null) {
-                System.err.println("WatchKey not recognized!!");
-                continue;
-            }
-
             for (WatchEvent<?> event: key.pollEvents()) {
                 WatchEvent.Kind kind = event.kind();
 
@@ -138,7 +122,7 @@
                 // Context for directory entry event is the file name of entry
                 WatchEvent<Path> ev = cast(event);
                 Path name = ev.context();
-                Path child = dir.resolve(name);
+                Path child = ((Path)key.watchable()).resolve(name);
 
                 // print out event
                 System.out.format("%s: %s\n", event.kind().name(), child);
@@ -156,15 +140,13 @@
                 }
             }
 
-            // reset key and remove from set if directory no longer accessible
+            // reset key
             boolean valid = key.reset();
             if (!valid) {
-                keys.remove(key);
-
-                // all directories are inaccessible
-                if (keys.isEmpty()) {
+                // directory no longer accessible
+                count--;
+                if (count == 0)
                     break;
-                }
             }
         }
     }
--- a/src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java	Sat Mar 19 14:21:52 2011 +0000
@@ -27,6 +27,7 @@
 
 import java.nio.file.attribute.*;
 import java.util.Map;
+import java.util.Set;
 import java.io.IOException;
 import sun.misc.Unsafe;
 
@@ -57,6 +58,10 @@
     private static final int DOS_XATTR_SYSTEM   = 0x04;
     private static final int DOS_XATTR_ARCHIVE  = 0x20;
 
+    // the names of the DOS attributes (includes basic)
+    private static final Set<String> dosAttributeNames =
+        Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME);
+
     LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
         super(file, followLinks);
     }
@@ -93,9 +98,10 @@
     public Map<String,Object> readAttributes(String[] attributes)
         throws IOException
     {
-        AttributesBuilder builder = AttributesBuilder.create(attributes);
+        AttributesBuilder builder =
+            AttributesBuilder.create(dosAttributeNames, attributes);
         DosFileAttributes attrs = readAttributes();
-        addBasicAttributesToBuilder(attrs, builder);
+        addRequestedBasicAttributes(attrs, builder);
         if (builder.match(READONLY_NAME))
             builder.add(READONLY_NAME, attrs.isReadOnly());
         if (builder.match(ARCHIVE_NAME))
--- a/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java	Sat Mar 19 14:21:52 2011 +0000
@@ -123,6 +123,10 @@
         private static final String OWNER_NAME = "owner";
         private static final String GROUP_NAME = "group";
 
+        // the names of the posix attributes (incudes basic)
+        static final Set<String> posixAttributeNames =
+            Util.newSet(basicAttributeNames, PERMISSIONS_NAME, OWNER_NAME, GROUP_NAME);
+
         Posix(UnixPath file, boolean followLinks) {
             super(file, followLinks);
         }
@@ -172,9 +176,10 @@
          * Invoked by readAttributes or sub-classes to add all matching posix
          * attributes to the builder
          */
-        final void addPosixAttributesToBuilder(PosixFileAttributes attrs,
+        final void addRequestedPosixAttributes(PosixFileAttributes attrs,
                                                AttributesBuilder builder)
         {
+            addRequestedBasicAttributes(attrs, builder);
             if (builder.match(PERMISSIONS_NAME))
                 builder.add(PERMISSIONS_NAME, attrs.permissions());
             if (builder.match(OWNER_NAME))
@@ -184,13 +189,13 @@
         }
 
         @Override
-        public Map<String,Object> readAttributes(String[] attributes)
+        public Map<String,Object> readAttributes(String[] requested)
             throws IOException
         {
-            AttributesBuilder builder = AttributesBuilder.create(attributes);
+            AttributesBuilder builder =
+                AttributesBuilder.create(posixAttributeNames, requested);
             PosixFileAttributes attrs = readAttributes();
-            addBasicAttributesToBuilder(attrs, builder);
-            addPosixAttributesToBuilder(attrs, builder);
+            addRequestedPosixAttributes(attrs, builder);
             return builder.unmodifiableMap();
         }
 
@@ -287,6 +292,12 @@
         private static final String GID_NAME = "gid";
         private static final String CTIME_NAME = "ctime";
 
+        // the names of the unix attributes (including posix)
+        static final Set<String> unixAttributeNames =
+            Util.newSet(posixAttributeNames,
+                        MODE_NAME, INO_NAME, DEV_NAME, RDEV_NAME,
+                        NLINK_NAME, UID_NAME, GID_NAME, CTIME_NAME);
+
         Unix(UnixPath file, boolean followLinks) {
             super(file, followLinks);
         }
@@ -316,13 +327,13 @@
         }
 
         @Override
-        public Map<String,Object> readAttributes(String[] attributes)
+        public Map<String,Object> readAttributes(String[] requested)
             throws IOException
         {
-            AttributesBuilder builder = AttributesBuilder.create(attributes);
+            AttributesBuilder builder =
+                AttributesBuilder.create(unixAttributeNames, requested);
             UnixFileAttributes attrs = readAttributes();
-            addBasicAttributesToBuilder(attrs, builder);
-            addPosixAttributesToBuilder(attrs, builder);
+            addRequestedPosixAttributes(attrs, builder);
             if (builder.match(MODE_NAME))
                 builder.add(MODE_NAME, attrs.mode());
             if (builder.match(INO_NAME))
--- a/src/solaris/classes/sun/nio/fs/UnixPath.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/solaris/classes/sun/nio/fs/UnixPath.java	Sat Mar 19 14:21:52 2011 +0000
@@ -606,7 +606,9 @@
 
     @Override
     public boolean startsWith(Path other) {
-        UnixPath that = toUnixPath(other);
+        if (!(Objects.requireNonNull(other) instanceof UnixPath))
+            return false;
+        UnixPath that = (UnixPath)other;
 
         // other path is longer
         if (that.path.length > path.length)
@@ -655,7 +657,9 @@
 
     @Override
     public boolean endsWith(Path other) {
-        UnixPath that = toUnixPath(other);
+        if (!(Objects.requireNonNull(other) instanceof UnixPath))
+            return false;
+        UnixPath that = (UnixPath)other;
 
         int thisLen = path.length;
         int thatLen = that.path.length;
--- a/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java	Sat Mar 19 14:21:52 2011 +0000
@@ -157,6 +157,11 @@
         private static final String HIDDEN_NAME = "hidden";
         private static final String ATTRIBUTES_NAME = "attributes";
 
+        // the names of the DOS attribtues (includes basic)
+        static final Set<String> dosAttributeNames =
+            Util.newSet(basicAttributeNames,
+                        READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME,  HIDDEN_NAME, ATTRIBUTES_NAME);
+
         Dos(WindowsPath file, boolean followLinks) {
             super(file, followLinks);
         }
@@ -193,9 +198,10 @@
         public Map<String,Object> readAttributes(String[] attributes)
             throws IOException
         {
-            AttributesBuilder builder = AttributesBuilder.create(attributes);
+            AttributesBuilder builder =
+                AttributesBuilder.create(dosAttributeNames, attributes);
             WindowsFileAttributes attrs = readAttributes();
-            addBasicAttributesToBuilder(attrs, builder);
+            addRequestedBasicAttributes(attrs, builder);
             if (builder.match(READONLY_NAME))
                 builder.add(READONLY_NAME, attrs.isReadOnly());
             if (builder.match(ARCHIVE_NAME))
--- a/src/windows/classes/sun/nio/fs/WindowsPath.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/src/windows/classes/sun/nio/fs/WindowsPath.java	Sat Mar 19 14:21:52 2011 +0000
@@ -646,7 +646,9 @@
 
     @Override
     public boolean startsWith(Path obj) {
-        WindowsPath other = toWindowsPath(obj);
+        if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
+            return false;
+        WindowsPath other = (WindowsPath)obj;
 
         // if this path has a root component the given path's root must match
         if (!this.root.equalsIgnoreCase(other.root)) {
@@ -675,7 +677,9 @@
 
     @Override
     public boolean endsWith(Path obj) {
-        WindowsPath other = toWindowsPath(obj);
+        if (!(Objects.requireNonNull(obj) instanceof WindowsPath))
+            return false;
+        WindowsPath other = (WindowsPath)obj;
 
         // other path is longer
         if (other.path.length() > this.path.length()) {
--- a/test/java/nio/file/Files/FileAttributes.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/test/java/nio/file/Files/FileAttributes.java	Sat Mar 19 14:21:52 2011 +0000
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 4313887 6838333
+ * @bug 4313887 6838333 7017446
  * @summary Unit test for java.nio.file.Files
  * @library ..
  */
@@ -94,12 +94,6 @@
         assertTrue(map.size() == 2);
         checkEqual(attrs.size(), map.get("size"));
         checkEqual(attrs.lastModifiedTime(), map.get("lastModifiedTime"));
-
-        map = Files.readAttributes(file,
-            "basic:lastModifiedTime,lastAccessTime,ShouldNotExist");
-        assertTrue(map.size() == 2);
-        checkEqual(attrs.lastModifiedTime(), map.get("lastModifiedTime"));
-        checkEqual(attrs.lastAccessTime(), map.get("lastAccessTime"));
     }
 
     // Exercise getAttribute/setAttribute/readAttributes on posix attributes
@@ -132,7 +126,7 @@
         assertTrue(map.size() >= 12);
         checkEqual(attrs.permissions(), map.get("permissions")); // check one
 
-        map = Files.readAttributes(file, "posix:size,owner,ShouldNotExist");
+        map = Files.readAttributes(file, "posix:size,owner");
         assertTrue(map.size() == 2);
         checkEqual(attrs.size(), map.get("size"));
         checkEqual(attrs.owner(), map.get("owner"));
@@ -155,7 +149,7 @@
         map = Files.readAttributes(file, "unix:*");
         assertTrue(map.size() >= 20);
 
-        map = Files.readAttributes(file, "unix:size,uid,gid,ShouldNotExist");
+        map = Files.readAttributes(file, "unix:size,uid,gid");
         assertTrue(map.size() == 3);
         checkEqual(map.get("size"),
                    Files.readAttributes(file, BasicFileAttributes.class).size());
@@ -206,14 +200,65 @@
         assertTrue(map.size() >= 13);
         checkEqual(attrs.isReadOnly(), map.get("readonly")); // check one
 
-        map = Files.readAttributes(file, "dos:size,hidden,ShouldNotExist");
+        map = Files.readAttributes(file, "dos:size,hidden");
         assertTrue(map.size() == 2);
         checkEqual(attrs.size(), map.get("size"));
         checkEqual(attrs.isHidden(), map.get("hidden"));
     }
 
+    static void checkBadSet(Path file, String attribute, Object value)
+        throws IOException
+    {
+        try {
+            Files.setAttribute(file, attribute, 0);
+            throw new RuntimeException("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ignore) { }
+    }
+
+    static void checkBadGet(Path file, String attribute) throws IOException {
+        try {
+            Files.getAttribute(file, attribute);
+            throw new RuntimeException("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ignore) { }
+    }
+
+    static void checkBadRead(Path file, String attribute) throws IOException {
+        try {
+            Files.readAttributes(file, attribute);
+            throw new RuntimeException("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ignore) { }
+    }
+
     static void miscTests(Path file) throws IOException {
-        // NPE tests
+        // unsupported views
+        try {
+            Files.setAttribute(file, "foo:bar", 0);
+            throw new RuntimeException("UnsupportedOperationException expected");
+        } catch (UnsupportedOperationException ignore) { }
+        try {
+            Files.getAttribute(file, "foo:bar");
+            throw new RuntimeException("UnsupportedOperationException expected");
+        } catch (UnsupportedOperationException ignore) { }
+        try {
+            Files.readAttributes(file, "foo:*");
+            throw new RuntimeException("UnsupportedOperationException expected");
+        } catch (UnsupportedOperationException ignore) { }
+
+        // bad args
+        checkBadSet(file, "", 0);
+        checkBadSet(file, "basic:", 0);
+        checkBadSet(file, "basic:foobar", 0);
+        checkBadGet(file, "");
+        checkBadGet(file, "basic:");
+        checkBadGet(file, "basic:foobar");
+        checkBadGet(file, "basic:size,lastModifiedTime");
+        checkBadGet(file, "basic:*");
+        checkBadRead(file, "");
+        checkBadRead(file, "basic:");
+        checkBadRead(file, "basic:foobar");
+        checkBadRead(file, "basic:size,foobar");
+
+        // nulls
         try {
             Files.getAttribute(file, null);
             throw new RuntimeException("NullPointerException expected");
--- a/test/java/nio/file/WatchService/Basic.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/test/java/nio/file/WatchService/Basic.java	Sat Mar 19 14:21:52 2011 +0000
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 4313887 6838333
+ * @bug 4313887 6838333 7017446
  * @summary Unit test for java.nio.file.WatchService
  * @library ..
  * @run main/timeout=120 Basic
@@ -44,6 +44,8 @@
     static void checkKey(WatchKey key, Path dir) {
         if (!key.isValid())
             throw new RuntimeException("Key is not valid");
+        if (key.watchable() != dir)
+            throw new RuntimeException("Unexpected watchable");
     }
 
     static void takeExpectedKey(WatchService watcher, WatchKey expected) {
--- a/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java	Fri Mar 18 19:46:02 2011 +0000
+++ b/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java	Sat Mar 19 14:21:52 2011 +0000
@@ -141,9 +141,6 @@
         map = Files.readAttributes(file, "user:*");
         if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME)))
             throw new RuntimeException("Unexpected attribute value");
-        map = Files.readAttributes(file, "user:DoesNotExist");
-        if (!map.isEmpty())
-            throw new RuntimeException("Map expected to be empty");
     }
 
     static void miscTests(final Path file) throws IOException {