commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject svn commit: r919682 - in /commons/proper/io/trunk/src: java/org/apache/commons/io/filefilter/FileFilterUtils.java java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java test/org/apache/commons/io/filefilter/FileFilterTestCase.java
Date Sat, 06 Mar 2010 01:26:06 GMT
Author: sebb
Date: Sat Mar  6 01:26:05 2010
New Revision: 919682

URL: http://svn.apache.org/viewvc?rev=919682&view=rev
Log:
IO-210 Create MagicNumberFileFilter
Applied patch, with following fixes:
- remove additional null checks in FileFilterUtils static methods; leave the checks to the
ctors
- close RandomAcessFile in MagicNumberFileFilter.accept()
- close streams in test cases
- Javadoc fix (wrap < in {@code})

Added:
    commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
  (with props)
Modified:
    commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/FileFilterUtils.java
    commons/proper/io/trunk/src/test/org/apache/commons/io/filefilter/FileFilterTestCase.java

Modified: commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/FileFilterUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/FileFilterUtils.java?rev=919682&r1=919681&r2=919682&view=diff
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/FileFilterUtils.java
(original)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/FileFilterUtils.java
Sat Mar  6 01:26:05 2010
@@ -279,6 +279,78 @@
         IOFileFilter maximumFilter = new SizeFileFilter(maxSizeInclusive + 1L, false);
         return new AndFileFilter(minimumFilter, maximumFilter);
     }
+    
+    /**
+     * Returns a filter that accepts files that begin with the provided magic
+     * number.
+     * 
+     * @param magicNumber the magic number (byte sequence) to match at the 
+     *        beginning of each file.
+     * 
+     * @return an IOFileFilter that accepts files beginning with the provided
+     *         magic number.
+     *         
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code> or the empty String.
+     */
+    public static IOFileFilter magicNumberFileFilter(String magicNumber) {
+        return new MagicNumberFileFilter(magicNumber);
+    }
+    
+    /**
+     * Returns a filter that accepts files that contains the provided magic
+     * number at a specified offset within the file.
+     * 
+     * @param magicNumber the magic number (byte sequence) to match at the 
+     *        provided offset in each file.
+     * @param offset the offset within the files to look for the magic number.
+     * 
+     * @return an IOFileFilter that accepts files containing the magic number
+     *         at the specified offset.
+     *         
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code> or the empty String, or if offset is a 
+     *         negative number.
+     */
+    public static IOFileFilter magicNumberFileFilter(String magicNumber, long offset) {
+        return new MagicNumberFileFilter(magicNumber, offset);
+    }
+    
+    /**
+     * Returns a filter that accepts files that begin with the provided magic
+     * number.
+     * 
+     * @param magicNumber the magic number (byte sequence) to match at the 
+     *        beginning of each file.
+     * 
+     * @return an IOFileFilter that accepts files beginning with the provided
+     *         magic number.
+     *         
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code> or is of length zero.
+     */
+    public static IOFileFilter magicNumberFileFilter(byte[] magicNumber) {
+        return new MagicNumberFileFilter(magicNumber);
+    }
+    
+    /**
+     * Returns a filter that accepts files that contains the provided magic
+     * number at a specified offset within the file.
+     * 
+     * @param magicNumber the magic number (byte sequence) to match at the 
+     *        provided offset in each file.
+     * @param offset the offset within the files to look for the magic number.
+     * 
+     * @return an IOFileFilter that accepts files containing the magic number
+     *         at the specified offset.
+     *         
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code>, or contains no bytes, or <code>offset</code>

+     *         is a negative number.
+     */
+    public static IOFileFilter magicNumberFileFilter(byte[] magicNumber, long offset) {
+        return new MagicNumberFileFilter(magicNumber, offset);
+    }
 
     //-----------------------------------------------------------------------
     /* Constructed on demand and then cached */

Added: commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java?rev=919682&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
(added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
Sat Mar  6 01:26:05 2010
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.filefilter;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * <p>
+ * File filter for matching files containing a "magic number". A magic number 
+ * is a unique series of bytes common to all files of a specific file format.
+ * For instance, all Java class files begin with the bytes 
+ * <code>0xCAFEBABE</code>. 
+ * </p>
+ * 
+ * <code><pre>
+ * File dir = new File(".");
+ * MagicNumberFileFilter javaClassFileFilter =
+ *     MagicNumberFileFilter(new byte[] {(byte) 0xCA, (byte) 0xFE, 
+ *       (byte) 0xBA, (byte) 0xBE}); 
+ * String[] javaClassFiles = dir.list(javaClassFileFilter);
+ * for (String javaClassFile : javaClassFiles) {
+ *     System.out.println(javaClassFile);
+ * }
+ * </pre></code>
+ * 
+ * <p>
+ * Sometimes, such as in the case of TAR files, the
+ * magic number will be offset by a certain number of bytes in the file. In the
+ * case of TAR archive files, this offset is 257 bytes.
+ * </p>
+ * 
+ * <code><pre>
+ * File dir = new File(".");
+ * MagicNumberFileFilter tarFileFilter = 
+ *     MagicNumberFileFilter("ustar", 257); 
+ * String[] tarFiles = dir.list(tarFileFilter);
+ * for (String tarFile : tarFiles) {
+ *     System.out.println(tarFile);
+ * }
+ * </pre></code>
+ * 
+ */
+public class MagicNumberFileFilter extends AbstractFileFilter implements
+        Serializable {
+    
+    /**
+     * The serialization version unique identifier.
+     */
+    private static final long serialVersionUID = -547733176983104172L;
+
+    /**
+     * The magic number to compare against the file's bytes at the provided 
+     * offset.
+     */
+    private final byte[] magicNumbers;
+    
+    /**
+     * The offset (in bytes) within the files that the magic number's bytes 
+     * should appear.
+     */
+    private final long byteOffset;
+    
+    /**
+     * <p>
+     * Constructs a new MagicNumberFileFilter and associates it with the magic
+     * number to test for in files. This constructor assumes a starting offset
+     * of <code>0</code>.
+     * </p>
+     * 
+     * <p>
+     * It is important to note that <em>the array is not cloned</em> and that
+     * any changes to the magic number array after construction will affect the
+     * behavior of this file filter.
+     * </p>
+     * 
+     * <code><pre>
+     * MagicNumberFileFilter javaClassFileFilter =
+     *     MagicNumberFileFilter(new byte[] {(byte) 0xCA, (byte) 0xFE, 
+     *       (byte) 0xBA, (byte) 0xBE}); 
+     * </pre></code>
+     * 
+     * @param magicNumber the magic number to look for in the file.
+     * 
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code>, or contains no bytes.
+     */
+    public MagicNumberFileFilter(byte[] magicNumber) {
+        this(magicNumber, 0);
+    }
+    
+    /**
+     * <p>
+     * Constructs a new MagicNumberFileFilter and associates it with the magic
+     * number to test for in files. This constructor assumes a starting offset
+     * of <code>0</code>.
+     * </p>
+     * 
+     * Example usage:
+     * <pre>
+     * {@code
+     * MagicNumberFileFilter xmlFileFilter = 
+     *     MagicNumberFileFilter("<?xml"); 
+     * }
+     * </pre>
+     * 
+     * @param magicNumber the magic number to look for in the file.
+     *        The string is converted to bytes using the platform default charset.
+     *
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code> or the empty String.
+     */
+    public MagicNumberFileFilter(String magicNumber) {
+        this(magicNumber, 0);
+    }
+    
+    /**
+     * <p>
+     * Constructs a new MagicNumberFileFilter and associates it with the magic
+     * number to test for in files and the byte offset location in the file to
+     * to look for that magic number.
+     * </p>
+     * 
+     * <code><pre>
+     * MagicNumberFileFilter tarFileFilter = 
+     *     MagicNumberFileFilter("ustar", 257); 
+     * </pre></code>
+     * 
+     * @param magicNumber the magic number to look for in the file. 
+     *        The string is converted to bytes using the platform default charset.
+     * @param offset the byte offset in the file to start comparing bytes.
+     * 
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code> or the empty String, or <code>offset</code>
is 
+     *         a negative number.
+     */
+    public MagicNumberFileFilter(String magicNumber, long offset) {
+        if (magicNumber == null) {
+            throw new IllegalArgumentException("The magic number cannot be null");
+        }
+        if (magicNumber.length() == 0) {
+            throw new IllegalArgumentException("The magic number must contain at least one
byte");
+        }
+        if (offset < 0) {
+            throw new IllegalArgumentException("The offset cannot be negative");
+        }
+        
+        this.magicNumbers = magicNumber.getBytes(); // uses the platform default charset
+        this.byteOffset = offset;
+    }
+    
+    /**
+     * <p>
+     * Constructs a new MagicNumberFileFilter and associates it with the magic
+     * number to test for in files and the byte offset location in the file to
+     * to look for that magic number.
+     * </p>
+     * 
+     * <p>
+     * It is important to note that <em>the array is not cloned</em> and that
+     * any changes to the magic number array after construction will affect the
+     * behavior of this file filter.
+     * </p>
+     * 
+     * <code><pre>
+     * MagicNumberFileFilter tarFileFilter =
+     *     MagicNumberFileFilter(new byte[] {0x75, 0x73, 0x74, 0x61, 0x72}, 257); 
+     * </pre></code>
+     * 
+     * <code><pre>
+     * MagicNumberFileFilter javaClassFileFilter =
+     *     MagicNumberFileFilter(new byte[] {0xCA, 0xFE, 0xBA, 0xBE}, 0); 
+     * </pre></code>
+     * 
+     * @param magicNumber the magic number to look for in the file.
+     * @param offset the byte offset in the file to start comparing bytes.
+     * 
+     * @throws IllegalArgumentException if <code>magicNumber</code> is 
+     *         <code>null</code>, or contains no bytes, or <code>offset</code>

+     *         is a negative number.
+     */
+    public MagicNumberFileFilter(byte[] magicNumber, long offset) {
+        if (magicNumber == null) {
+            throw new IllegalArgumentException("The magic number cannot be null");
+        }
+        if (magicNumber.length == 0) {
+            throw new IllegalArgumentException("The magic number must contain at least one
byte");
+        }
+        if (offset < 0) {
+            throw new IllegalArgumentException("The offset cannot be negative");
+        }
+        
+        this.magicNumbers = magicNumber;
+        this.byteOffset = offset;
+    }
+    
+    /**
+     * <p>
+     * Accepts the provided file if the file contains the file filter's magic
+     * number at the specified offset.
+     * </p>
+     * 
+     * <p>
+     * If any {@link IOException}s occur while reading the file, the file will
+     * be rejected.
+     * </p>
+     * 
+     * @param file the file to accept or reject.
+     * 
+     * @return <code>true</code> if the file contains the filter's magic number

+     *         at the specified offset, <code>false</code> otherwise.
+     */
+    @Override
+    public boolean accept(File file) {
+        if (file != null && file.isFile() && file.canRead()) {
+            RandomAccessFile randomAccessFile = null;
+            try {
+                byte[] fileBytes = new byte[this.magicNumbers.length]; 
+                randomAccessFile = new RandomAccessFile(file, "r");
+                randomAccessFile.seek(byteOffset);
+                randomAccessFile.read(fileBytes);
+                return Arrays.equals(this.magicNumbers, fileBytes);
+            } catch (IOException ioe) {
+                // Do nothing, fall through and do not accept file
+            } finally {
+                IOUtils.closeQuietly(randomAccessFile);
+            }
+        }
+        
+        return false;
+    }
+
+    /**
+     * Returns a String representation of the file filter, which includes the 
+     * magic number bytes and byte offset.
+     * 
+     * @return a String representation of the file filter.
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(super.toString());
+        builder.append("(");
+        builder.append(new String(magicNumbers));// TODO perhaps use hex if value is not
printable
+        builder.append(",");
+        builder.append(this.byteOffset);
+        builder.append(")");
+        return builder.toString();
+    }
+}

Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Modified: commons/proper/io/trunk/src/test/org/apache/commons/io/filefilter/FileFilterTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/org/apache/commons/io/filefilter/FileFilterTestCase.java?rev=919682&r1=919681&r2=919682&view=diff
==============================================================================
--- commons/proper/io/trunk/src/test/org/apache/commons/io/filefilter/FileFilterTestCase.java
(original)
+++ commons/proper/io/trunk/src/test/org/apache/commons/io/filefilter/FileFilterTestCase.java
Sat Mar  6 01:26:05 2010
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.io.FileFilter;
 import java.io.FilenameFilter;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
@@ -26,6 +27,7 @@
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOCase;
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.testtools.FileBasedTestCase;
 
 /**
@@ -443,6 +445,7 @@
         assertEquals(true, f.getFileFilters().isEmpty());
     }
 
+    @SuppressWarnings("deprecation")
     public void testDeprecatedWildcard() throws Exception {
         IOFileFilter filter = new WildcardFilter("*.txt");
         List<String> patternList = Arrays.asList( new String[] { "*.txt", "*.xml",
"*.gif" } );
@@ -869,5 +872,131 @@
         fileA.delete();
         fileB.delete();
     }
-         
+    
+    //-----------------------------------------------------------------------
+    
+    public void testMagicNumberFileFilterBytes() throws Exception {
+        byte[] classFileMagicNumber = 
+            new byte[] {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
+        String xmlFileContent = "<?xml version=\"1.0\" encoding=\"UTF-8\">\n" +
+            "<element>text</element>";
+        
+        File classFileA = new File(getTestDirectory(), "A.class");
+        File xmlFileB = new File(getTestDirectory(), "B.xml");
+        File dir = new File(getTestDirectory(), "D");
+        dir.mkdirs();
+        
+        OutputStream classFileAStream = FileUtils.openOutputStream(classFileA);
+        IOUtils.write(classFileMagicNumber, classFileAStream);
+        generateTestData(classFileAStream, 32);
+        classFileAStream.close();
+        
+        FileUtils.write(xmlFileB, xmlFileContent);
+        
+        IOFileFilter filter = new MagicNumberFileFilter(classFileMagicNumber);
+        
+        assertFiltering(filter, classFileA, true);
+        assertFiltering(filter, xmlFileB, false);
+        assertFiltering(filter, dir, false);
+        
+        filter = FileFilterUtils.magicNumberFileFilter(classFileMagicNumber);
+        
+        assertFiltering(filter, classFileA, true);
+        assertFiltering(filter, xmlFileB, false);
+        assertFiltering(filter, dir, false);
+    }
+    
+    public void testMagicNumberFileFilterBytesOffset() throws Exception {
+        byte[] tarMagicNumber = new byte[] {0x75, 0x73, 0x74, 0x61, 0x72};
+        long tarMagicNumberOffset = 257;
+        
+        File tarFileA = new File(getTestDirectory(), "A.tar");
+        File randomFileB = new File(getTestDirectory(), "B.txt");
+        File dir = new File(getTestDirectory(), "D");
+        dir.mkdirs();
+        
+        OutputStream tarFileAStream = FileUtils.openOutputStream(tarFileA);
+        generateTestData(tarFileAStream, tarMagicNumberOffset);
+        IOUtils.write(tarMagicNumber, tarFileAStream);
+        tarFileAStream.close();
+        
+        createFile(randomFileB, 2 * tarMagicNumberOffset);
+        
+        IOFileFilter filter = 
+            new MagicNumberFileFilter(tarMagicNumber, tarMagicNumberOffset);
+        
+        assertFiltering(filter, tarFileA, true);
+        assertFiltering(filter, randomFileB, false);
+        assertFiltering(filter, dir, false);
+        
+        filter = FileFilterUtils.magicNumberFileFilter(tarMagicNumber, 
+                tarMagicNumberOffset);
+        
+        assertFiltering(filter, tarFileA, true);
+        assertFiltering(filter, randomFileB, false);
+        assertFiltering(filter, dir, false);
+    }
+    
+    public void testMagicNumberFileFilterString() throws Exception {
+        byte[] classFileMagicNumber = 
+            new byte[] {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
+        String xmlFileContent = "<?xml version=\"1.0\" encoding=\"UTF-8\">\n" +
+            "<element>text</element>";
+        String xmlMagicNumber = "<?xml version=\"1.0\"";
+        
+        File classFileA = new File(getTestDirectory(), "A.class");
+        File xmlFileB = new File(getTestDirectory(), "B.xml");
+        File dir = new File(getTestDirectory(), "D");
+        dir.mkdirs();
+        
+        OutputStream classFileAStream = FileUtils.openOutputStream(classFileA);
+        IOUtils.write(classFileMagicNumber, classFileAStream);
+        generateTestData(classFileAStream, 32);
+        classFileAStream.close();
+        
+        FileUtils.write(xmlFileB, xmlFileContent);
+        
+        IOFileFilter filter = new MagicNumberFileFilter(xmlMagicNumber);
+        
+        assertFiltering(filter, classFileA, false);
+        assertFiltering(filter, xmlFileB, true);
+        assertFiltering(filter, dir, false);
+        
+        filter = FileFilterUtils.magicNumberFileFilter(xmlMagicNumber);
+        
+        assertFiltering(filter, classFileA, false);
+        assertFiltering(filter, xmlFileB, true);
+        assertFiltering(filter, dir, false);
+    }
+    
+    public void testMagicNumberFileFilterStringOffset() throws Exception {
+        String tarMagicNumber = "ustar";
+        long tarMagicNumberOffset = 257;
+        
+        File tarFileA = new File(getTestDirectory(), "A.tar");
+        File randomFileB = new File(getTestDirectory(), "B.txt");
+        File dir = new File(getTestDirectory(), "D");
+        dir.mkdirs();
+        
+        OutputStream tarFileAStream = FileUtils.openOutputStream(tarFileA);
+        generateTestData(tarFileAStream, tarMagicNumberOffset);
+        IOUtils.write(tarMagicNumber, tarFileAStream);
+        tarFileAStream.close();
+
+        createFile(randomFileB, 2 * tarMagicNumberOffset);
+        
+        IOFileFilter filter = 
+            new MagicNumberFileFilter(tarMagicNumber, tarMagicNumberOffset);
+        
+        assertFiltering(filter, tarFileA, true);
+        assertFiltering(filter, randomFileB, false);
+        assertFiltering(filter, dir, false);
+        
+        filter = FileFilterUtils.magicNumberFileFilter(tarMagicNumber, 
+                tarMagicNumberOffset);
+        
+        assertFiltering(filter, tarFileA, true);
+        assertFiltering(filter, randomFileB, false);
+        assertFiltering(filter, dir, false);
+    }
 }



Mime
View raw message