changeset 785:44f2691ef9be

Updates to DirectoryStreamFilters - allOf/anyOf not safe (Thanks to Remi Forax for this) - newContentType can make sure of type inference (also suggested by Remi) - improve test coverage
author alanb
date Sat, 25 Oct 2008 13:13:44 +0100
parents f9c307f6f0ff
children 9889b32ad574
files src/share/classes/java/nio/file/DirectoryStreamFilters.java test/java/nio/file/DirectoryStream/Filters.java
diffstat 2 files changed, 223 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/nio/file/DirectoryStreamFilters.java	Sat Oct 25 12:05:27 2008 +0100
+++ b/src/share/classes/java/nio/file/DirectoryStreamFilters.java	Sat Oct 25 13:13:44 2008 +0100
@@ -30,8 +30,8 @@
 import sun.nio.fs.MimeType;
 
 /**
- * This class consists exclusively of static methods that construct directory
- * stream filters or combine filters into simple expressions.
+ * This class consists exclusively of static methods that construct or combine
+ * filters.
  *
  * @since 1.7
  */
@@ -86,13 +86,15 @@
      *          If the {@code type} parameter cannot be parsed as a MIME type
      *          or it has parameters
      */
-    public static DirectoryStream.Filter<FileRef> newContentTypeFilter(String type) {
+    public static <T extends FileRef> DirectoryStream.Filter<T>
+        newContentTypeFilter(String type)
+    {
         final MimeType matchType = MimeType.parse(type);
         if (matchType.hasParameters())
             throw new IllegalArgumentException("Parameters not allowed");
-        return new DirectoryStream.Filter<FileRef>() {
+        return new DirectoryStream.Filter<T>() {
             @Override
-            public boolean accept(FileRef entry) {
+            public boolean accept(T entry) {
                 String fileType;
                 try {
                     fileType = Files.probeContentType(entry);
@@ -108,32 +110,38 @@
     }
 
     /**
-     * Returns a directory stream filter that {@link
-     * java.nio.file.DirectoryStream.Filter#accept accepts} a directory entry
-     * if the entry is accepted by all of the given filters.
+     * Returns a directory stream filter that {@link DirectoryStream.Filter#accept
+     * accepts} a directory entry if the entry is accepted by all of the given
+     * filters.
      *
-     * <p> This method returns a filter that invokes, in turn, the {@code accept}
-     * method of each of the given filters. If {@code false} is returned by any
-     * of the filters then the directory entry is filtered. If the directory
-     * entry is not filtered then the resulting filter accepts the entry.
-     * If the array of filters contains zero elements then the resulting
-     * filter accepts all directory entries.
+     * <p> This method returns a filter that invokes, in iterator order, the
+     * {@code accept} method of each of the filters. If {@code false} is returned
+     * by any of the filters then the directory entry is filtered. If the
+     * directory entry is not filtered then the resulting filter accepts the
+     * entry. If the iterator returns zero elements then the resulting filter
+     * accepts all directory entries.
+     *
+     * <p> <b>Usage Example:</b>
+     * <pre>
+     *     List&lt;DirectoryStream.Filter&lt;? super Path&gt;&gt; filters = ...
+     *     DirectoryStream.Filter&lt;Path&gt; filter = DirectoryStreamFilters.allOf(filters);
+     * </pre>
      *
      * @param   filters
-     *          The array of filters
+     *          The sequence of filters
      *
      * @return  The resulting filter
      */
-    @SuppressWarnings("unchecked")
     public static <T extends FileRef> DirectoryStream.Filter<T>
-        allOf(final DirectoryStream.Filter<? extends T>... filters)
+        allOf(final Iterable<? extends DirectoryStream.Filter<? super T>> filters)
     {
+        if (filters == null)
+            throw new NullPointerException("'filters' is null");
         return new DirectoryStream.Filter<T>() {
             @Override
             public boolean accept(T entry) {
-                for (DirectoryStream.Filter<? extends T> filter: filters) {
-                    DirectoryStream.Filter<T> f = (DirectoryStream.Filter<T>)filter;
-                    if (!f.accept(entry))
+                for (DirectoryStream.Filter<? super T> filter: filters) {
+                    if (!filter.accept(entry))
                         return false;
                 }
                 return true;
@@ -142,32 +150,32 @@
     }
 
     /**
-     * Returns a directory stream filter that {@link
-     * java.nio.file.DirectoryStream.Filter#accept accepts} a directory entry
-     * if the entry is accepted by one or more of the given filters.
+     * Returns a directory stream filter that {@link DirectoryStream.Filter#accept
+     * accepts} a directory entry if the entry is accepted by one or more of
+     * the given filters.
      *
-     * <p> This method returns a filter that invokes, in turn, the {@code accept}
-     * method of each of the given filters. If {@code true} is returned by any
-     * of the filters then the directory entry is accepted. If none of the
-     * filters accepts the directory entry then it is filtered. If the array of
-     * filters contains zero elements then the resulting filter will filter all
-     * directory entries.
+     * <p> This method returns a filter that invokes, in iteration order, the
+     * {@code accept} method of each of filter. If {@code true} is returned by
+     * any of the filters then the directory entry is accepted. If none of the
+     * filters accepts the directory entry then it is filtered. If the iterator
+     * returns zero elements then the resulting filter filters all directory
+     * entries.
      *
      * @param   filters
-     *          The array of filters
+     *          The sequence of filters
      *
      * @return  The resulting filter
      */
-    @SuppressWarnings("unchecked")
-    public static <T extends FileRef> DirectoryStream.Filter
-        anyOf(final DirectoryStream.Filter<? extends T>... filters)
+    public static <T extends FileRef> DirectoryStream.Filter<T>
+        anyOf(final Iterable<? extends DirectoryStream.Filter<? super T>> filters)
     {
+        if (filters == null)
+            throw new NullPointerException("'filters' is null");
         return new DirectoryStream.Filter<T>() {
             @Override
             public boolean accept(T entry) {
-                for (DirectoryStream.Filter<? extends T> filter: filters) {
-                    DirectoryStream.Filter<T> f = (DirectoryStream.Filter<T>)filter;
-                    if (f.accept(entry))
+                for (DirectoryStream.Filter<? super T> filter: filters) {
+                    if (filter.accept(entry))
                         return true;
                 }
                 return false;
@@ -190,6 +198,8 @@
     public static <T extends FileRef> DirectoryStream.Filter<T>
         complementOf(final DirectoryStream.Filter<T> filter)
     {
+        if (filter == null)
+            throw new NullPointerException("'filter' is null");
         return new DirectoryStream.Filter<T>() {
             @Override
             public boolean accept(T entry) {
--- a/test/java/nio/file/DirectoryStream/Filters.java	Sat Oct 25 12:05:27 2008 +0100
+++ b/test/java/nio/file/DirectoryStream/Filters.java	Sat Oct 25 13:13:44 2008 +0100
@@ -21,161 +21,219 @@
  * have any questions.
  */
 
+/* @test
+ * @bug 4313887
+ * @summary Unit test for java.nio.file.DirectoryStreamFilters
+ * @library ..
+ */
+
 import java.nio.file.*;
+import static java.nio.file.DirectoryStreamFilters.*;
+import java.nio.file.attribute.Attributes;
 import java.io.*;
-import java.util.Random;
-import java.util.regex.*;
+import java.util.*;
 
 public class Filters {
-
     static final Random rand = new Random();
 
-    static char next = 'A';
-
-    static String generateName() {
-        int len = 3 + rand.nextInt(20);
-        StringBuilder sb = new StringBuilder(len+1);
-        sb.append(next++);
-        for (int i=0; i<len; i++) {
-            if (rand.nextBoolean())
-                sb.append((char)('A' + rand.nextInt(26)));
-            else
-                sb.append((char)('a' + rand.nextInt(26)));
-        }
-        sb.append(".html");
-        return sb.toString();
+    // returns a filter that only accepts files that are larger than a given size
+    static DirectoryStream.Filter<FileRef> newMinimumSizeFilter(final long min) {
+        return new DirectoryStream.Filter<FileRef>() {
+            public boolean accept(FileRef file) {
+                try {
+                    long size = Attributes.readBasicFileAttributes(file, true).size();
+                    return size >= min;
+                } catch (IOException e) {
+                    throw new IOError(e);
+                }
+            }
+        };
     }
 
-    static Path[] setup(Path dir) throws IOException {
-        // create 10-25 files.
-        final int n = 10 + rand.nextInt(16);
-        Path[] names = new Path[n];
-        for (int i=0; i<n; i++) {
-            Path file = dir.resolve(generateName());
+    // returns a filter that only accepts files that are matched by a given glob
+    static DirectoryStream.Filter<Path> newGlobFilter(final String glob) {
+        return new DirectoryStream.Filter<Path>() {
+            PathMatcher matcher = FileSystems.getDefault().getNameMatcher("glob", glob);
+            public boolean accept(Path file) {
+                return matcher.matches(file);
+            }
+        };
+    }
+
+    static final int BIG_FILE_THRESHOLD = 8192;
+
+    static int totalCount;
+    static int htmlCount;
+    static int bigAndHtmlCount;
+    static int bigOrHtmlCount;
+
+    // generates random files in the test directory and initializes the counts
+    static void setup(Path dir) throws IOException {
+        // create 10-26 files.
+        totalCount = 10 + rand.nextInt(17);
+        char firstChar = 'A';
+        for (int i=0; i<totalCount; i++) {
+            boolean isHtml = rand.nextBoolean();
+            boolean isBig = rand.nextBoolean();
+            if (isHtml) {
+                htmlCount++;
+                if (isBig) bigAndHtmlCount++;
+            }
+            if (isHtml || isBig)
+                bigOrHtmlCount++;
+            String name;
+            if (isHtml) {
+                name = firstChar + ".html";
+            } else {
+                name = firstChar + ".tmp";
+            }
+            firstChar++;
+            int size = rand.nextInt(BIG_FILE_THRESHOLD);
+            if (isBig)
+                size += BIG_FILE_THRESHOLD;
+            Path file = dir.resolve(name);
             OutputStream out = file.newOutputStream();
             try {
-                out.write("<html></html>".getBytes());
+                if (size > 0)
+                    out.write(new byte[size]);
             } finally {
                 out.close();
             }
-            // actual filename
-            names[i] = file.toRealPath(true).getName();
-            System.out.println(names[i]);
+            System.out.format("Created %s, size %d byte(s)\n", name, size);
         }
-        return names;
     }
 
-    static int matchCount(Path dir, DirectoryStream.Filter filter)
-        throws IOException
-    {
-        int count = 0;
-        DirectoryStream<Path> stream = dir.newDirectoryStream(filter);
+    static boolean isHtml(Path file) {
+        return file.toString().endsWith(".html");
+    }
+
+    static boolean isBig(Path file) throws IOException {
+        long size = Attributes.readBasicFileAttributes(file, true).size();
+        return size >= BIG_FILE_THRESHOLD;
+    }
+
+    static void checkCount(int expected, int actual) {
+        if (actual != expected)
+            throw new RuntimeException("'" + expected +
+                "' entries expected, actual: " + actual);
+    }
+
+    static void doTests(Path dir) throws IOException {
+        final List<DirectoryStream.Filter<Path>> emptyList = Collections.emptyList();
+
+        // list containing two filters
+        List<DirectoryStream.Filter<? super Path>> filters =
+            new ArrayList<DirectoryStream.Filter<? super Path>>();
+        filters.add(newMinimumSizeFilter(BIG_FILE_THRESHOLD));
+        filters.add(newGlobFilter("*.html"));
+
+        int accepted;
+        DirectoryStream<Path> stream;
+
+        System.out.println("Test: newContentTypeFilter");
+        accepted = 0;
+        stream = dir.newDirectoryStream(newContentTypeFilter("text/html"));
         try {
             for (Path entry: stream) {
-                count++;
+                if (!isHtml(entry))
+                    throw new RuntimeException("html file expected");
+                accepted++;
             }
         } finally {
             stream.close();
         }
-        return count;
-    }
+        checkCount(htmlCount, accepted);
 
-    static void checkResult(int actual, int expected) {
-        System.out.format("%d matched, %d expected%n", actual, expected);
-        if (actual != expected)
-            throw new RuntimeException();
-    }
+        System.out.println("Test: allOf with list of filters");
+        accepted = 0;
+        stream = dir.newDirectoryStream(allOf(filters));
+        try {
+            for (Path entry: stream) {
+                if (!isHtml(entry))
+                    throw new RuntimeException("html file expected");
+                if (!isBig(entry))
+                    throw new RuntimeException("big file expected");
+                accepted++;
+            }
+        } finally {
+            stream.close();
+        }
+        checkCount(bigAndHtmlCount, accepted);
 
-    static void doTests(Path dir, Path[] names)
-        throws IOException
-    {
-        DirectoryStream.Filter<Path> filter;
-        int actual, expected;
+        System.out.println("Test: allOf with empty list");
+        accepted = 0;
+        stream = dir.newDirectoryStream(allOf(emptyList));
+        try {
+            for (Path entry: stream) {
+                accepted++;
+            }
+        } finally {
+            stream.close();
+        }
+        checkCount(totalCount, accepted);
 
-        // --
+        System.out.println("Test: anyOf with list of filters");
+        accepted = 0;
+        stream = dir.newDirectoryStream(anyOf(filters));
+        try {
+            for (Path entry: stream) {
+                if (!isHtml(entry) && !isBig(entry))
+                    throw new RuntimeException("html or big file expected");
+                accepted++;
+            }
+        } finally {
+            stream.close();
+        }
+        checkCount(bigOrHtmlCount, accepted);
 
-        System.out.println("-- case sensitive *.html --");
-        filter = DirectoryStreamFilters.newCaseSensitiveGlobFilter("*.html");
-        expected = 0;
-        for (Path name: names) {
-            if (name.toString().endsWith(".html")) expected++;
+        System.out.println("Test: anyOf with empty list");
+        accepted = 0;
+        stream = dir.newDirectoryStream(anyOf(emptyList));
+        try {
+            for (Path entry: stream) {
+                accepted++;
+            }
+        } finally {
+            stream.close();
         }
-        actual = matchCount(dir, filter);
-        checkResult(actual, expected);
+        checkCount(0, accepted);
 
-        // --
+        System.out.println("Test: complementOf");
+        accepted = 0;
+        stream = dir.newDirectoryStream(complementOf(newGlobFilter("*.html")));
+        try {
+            for (Path entry: stream) {
+                accepted++;
+            }
+        } finally {
+            stream.close();
+        }
+        checkCount(totalCount-htmlCount, accepted);
 
-        System.out.println("-- case sensitive *.?? --");
-        filter = DirectoryStreamFilters.newCaseSensitiveGlobFilter("*.??");
-        actual = matchCount(dir, filter);
-        checkResult(actual, 0);
-
-        // --
-
-        System.out.println("-- case sensitive *.???? --");
-        filter = DirectoryStreamFilters.newCaseSensitiveGlobFilter("*.????");
-        actual = matchCount(dir, filter);
-        checkResult(actual, names.length);
-
-        // --
-
-        System.out.println("-- case sensitive *A* --");
-        filter = DirectoryStreamFilters.newCaseSensitiveGlobFilter("*A*");
-        expected = 0;
-        for (Path name: names) {
-            if (name.toString().indexOf('A') >= 0) expected++;
-        }
-        actual = matchCount(dir, filter);
-        checkResult(actual, expected);
-
-        // --
-
-        System.out.println("-- case insensitive *.HtMl --");
-        filter = DirectoryStreamFilters.newCaseInsensitiveGlobFilter("*.HtMl");
-        actual = matchCount(dir, filter);
-        checkResult(actual, names.length);
-
-        System.out.println("-- case insensitive a* --");
-        filter = DirectoryStreamFilters.newCaseInsensitiveGlobFilter("a*");
-        actual = matchCount(dir, filter);
-        checkResult(actual, 1);
-
-        System.out.println("-- case insensitive A* --");
-        filter = DirectoryStreamFilters.newCaseInsensitiveGlobFilter("A*");
-        actual = matchCount(dir, filter);
-        checkResult(actual, 1);
-
-        // --
-
-        System.out.println("-- content type text/html --");
-        DirectoryStream.Filter f = DirectoryStreamFilters
-            .newContentTypeFilter("text/html");
-        actual = matchCount(dir, f);
-        // skip check if content type cannot be guessed
-        if (actual > 0)
-            checkResult(actual, names.length);
-
-        // -- IllegalArgumentException --
-
+        System.out.println("Test: nulls");
         try {
-            DirectoryStreamFilters.newContentTypeFilter("foo");
-            throw new RuntimeException("IllegalArgumentException expected");
-        } catch (IllegalArgumentException e) {
-        }
-
+            newContentTypeFilter(null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
         try {
-            DirectoryStreamFilters.newContentTypeFilter("text/plain;char=UTF-8");
-            throw new RuntimeException("IllegalArgumentException expected");
-        } catch (IllegalArgumentException e) {
-        }
+            allOf(null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
+        try {
+            anyOf(null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
+        try {
+            complementOf(null);
+            throw new RuntimeException("NullPointerException expected");
+        } catch (NullPointerException npe) { }
     }
 
     public static void main(String[] args) throws IOException {
         Path dir = TestUtil.createTemporaryDirectory();
         try {
-            Path[] names = setup(dir);
-            System.out.format("Created %d files.%n", names.length);
-            doTests(dir, names);
+            setup(dir);
+            doTests(dir);
         } finally {
             TestUtil.removeAll(dir);
         }