~ubuntu-branches/ubuntu/saucy/commons-io/saucy

« back to all changes in this revision

Viewing changes to src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java

  • Committer: Package Import Robot
  • Author(s): tony mancill
  • Date: 2013-05-18 10:40:54 UTC
  • mfrom: (1.1.4) (3.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20130518104054-wqbtjam30uyseu9j
Tags: 2.4-2
* Team upload.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
 
3
 * contributor license agreements.  See the NOTICE file distributed with
 
4
 * this work for additional information regarding copyright ownership.
 
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
6
 * (the "License"); you may not use this file except in compliance with
 
7
 * the License.  You may obtain a copy of the License at
 
8
 * 
 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
 
10
 * 
 
11
 * Unless required by applicable law or agreed to in writing, software
 
12
 * distributed under the License is distributed on an "AS IS" BASIS,
 
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
14
 * See the License for the specific language governing permissions and
 
15
 * limitations under the License.
 
16
 */
 
17
package org.apache.commons.io.filefilter;
 
18
 
 
19
import java.io.File;
 
20
import java.io.IOException;
 
21
import java.io.RandomAccessFile;
 
22
import java.io.Serializable;
 
23
import java.util.Arrays;
 
24
 
 
25
import org.apache.commons.io.IOUtils;
 
26
 
 
27
/**
 
28
 * <p>
 
29
 * File filter for matching files containing a "magic number". A magic number 
 
30
 * is a unique series of bytes common to all files of a specific file format.
 
31
 * For instance, all Java class files begin with the bytes 
 
32
 * <code>0xCAFEBABE</code>. 
 
33
 * </p>
 
34
 * 
 
35
 * <code><pre>
 
36
 * File dir = new File(".");
 
37
 * MagicNumberFileFilter javaClassFileFilter =
 
38
 *     MagicNumberFileFilter(new byte[] {(byte) 0xCA, (byte) 0xFE, 
 
39
 *       (byte) 0xBA, (byte) 0xBE}); 
 
40
 * String[] javaClassFiles = dir.list(javaClassFileFilter);
 
41
 * for (String javaClassFile : javaClassFiles) {
 
42
 *     System.out.println(javaClassFile);
 
43
 * }
 
44
 * </pre></code>
 
45
 * 
 
46
 * <p>
 
47
 * Sometimes, such as in the case of TAR files, the
 
48
 * magic number will be offset by a certain number of bytes in the file. In the
 
49
 * case of TAR archive files, this offset is 257 bytes.
 
50
 * </p>
 
51
 * 
 
52
 * <code><pre>
 
53
 * File dir = new File(".");
 
54
 * MagicNumberFileFilter tarFileFilter = 
 
55
 *     MagicNumberFileFilter("ustar", 257); 
 
56
 * String[] tarFiles = dir.list(tarFileFilter);
 
57
 * for (String tarFile : tarFiles) {
 
58
 *     System.out.println(tarFile);
 
59
 * }
 
60
 * </pre></code>
 
61
 * @since 2.0
 
62
 * @see FileFilterUtils#magicNumberFileFilter(byte[])
 
63
 * @see FileFilterUtils#magicNumberFileFilter(String)
 
64
 * @see FileFilterUtils#magicNumberFileFilter(byte[], long)
 
65
 * @see FileFilterUtils#magicNumberFileFilter(String, long)
 
66
 */
 
67
public class MagicNumberFileFilter extends AbstractFileFilter implements
 
68
        Serializable {
 
69
    
 
70
    /**
 
71
     * The serialization version unique identifier.
 
72
     */
 
73
    private static final long serialVersionUID = -547733176983104172L;
 
74
 
 
75
    /**
 
76
     * The magic number to compare against the file's bytes at the provided 
 
77
     * offset.
 
78
     */
 
79
    private final byte[] magicNumbers;
 
80
    
 
81
    /**
 
82
     * The offset (in bytes) within the files that the magic number's bytes 
 
83
     * should appear.
 
84
     */
 
85
    private final long byteOffset;
 
86
    
 
87
    /**
 
88
     * <p>
 
89
     * Constructs a new MagicNumberFileFilter and associates it with the magic
 
90
     * number to test for in files. This constructor assumes a starting offset
 
91
     * of <code>0</code>.
 
92
     * </p>
 
93
     * 
 
94
     * <p>
 
95
     * It is important to note that <em>the array is not cloned</em> and that
 
96
     * any changes to the magic number array after construction will affect the
 
97
     * behavior of this file filter.
 
98
     * </p>
 
99
     * 
 
100
     * <code><pre>
 
101
     * MagicNumberFileFilter javaClassFileFilter =
 
102
     *     MagicNumberFileFilter(new byte[] {(byte) 0xCA, (byte) 0xFE, 
 
103
     *       (byte) 0xBA, (byte) 0xBE}); 
 
104
     * </pre></code>
 
105
     * 
 
106
     * @param magicNumber the magic number to look for in the file.
 
107
     * 
 
108
     * @throws IllegalArgumentException if <code>magicNumber</code> is 
 
109
     *         {@code null}, or contains no bytes.
 
110
     */
 
111
    public MagicNumberFileFilter(byte[] magicNumber) {
 
112
        this(magicNumber, 0);
 
113
    }
 
114
    
 
115
    /**
 
116
     * <p>
 
117
     * Constructs a new MagicNumberFileFilter and associates it with the magic
 
118
     * number to test for in files. This constructor assumes a starting offset
 
119
     * of <code>0</code>.
 
120
     * </p>
 
121
     * 
 
122
     * Example usage:
 
123
     * <pre>
 
124
     * {@code
 
125
     * MagicNumberFileFilter xmlFileFilter = 
 
126
     *     MagicNumberFileFilter("<?xml"); 
 
127
     * }
 
128
     * </pre>
 
129
     * 
 
130
     * @param magicNumber the magic number to look for in the file.
 
131
     *        The string is converted to bytes using the platform default charset.
 
132
     *
 
133
     * @throws IllegalArgumentException if <code>magicNumber</code> is 
 
134
     *         {@code null} or the empty String.
 
135
     */
 
136
    public MagicNumberFileFilter(String magicNumber) {
 
137
        this(magicNumber, 0);
 
138
    }
 
139
    
 
140
    /**
 
141
     * <p>
 
142
     * Constructs a new MagicNumberFileFilter and associates it with the magic
 
143
     * number to test for in files and the byte offset location in the file to
 
144
     * to look for that magic number.
 
145
     * </p>
 
146
     * 
 
147
     * <code><pre>
 
148
     * MagicNumberFileFilter tarFileFilter = 
 
149
     *     MagicNumberFileFilter("ustar", 257); 
 
150
     * </pre></code>
 
151
     * 
 
152
     * @param magicNumber the magic number to look for in the file. 
 
153
     *        The string is converted to bytes using the platform default charset.
 
154
     * @param offset the byte offset in the file to start comparing bytes.
 
155
     * 
 
156
     * @throws IllegalArgumentException if <code>magicNumber</code> is 
 
157
     *         {@code null} or the empty String, or <code>offset</code> is 
 
158
     *         a negative number.
 
159
     */
 
160
    public MagicNumberFileFilter(String magicNumber, long offset) {
 
161
        if (magicNumber == null) {
 
162
            throw new IllegalArgumentException("The magic number cannot be null");
 
163
        }
 
164
        if (magicNumber.length() == 0) {
 
165
            throw new IllegalArgumentException("The magic number must contain at least one byte");
 
166
        }
 
167
        if (offset < 0) {
 
168
            throw new IllegalArgumentException("The offset cannot be negative");
 
169
        }
 
170
        
 
171
        this.magicNumbers = magicNumber.getBytes(); // uses the platform default charset
 
172
        this.byteOffset = offset;
 
173
    }
 
174
    
 
175
    /**
 
176
     * <p>
 
177
     * Constructs a new MagicNumberFileFilter and associates it with the magic
 
178
     * number to test for in files and the byte offset location in the file to
 
179
     * to look for that magic number.
 
180
     * </p>
 
181
     * 
 
182
     * <p>
 
183
     * It is important to note that <em>the array is not cloned</em> and that
 
184
     * any changes to the magic number array after construction will affect the
 
185
     * behavior of this file filter.
 
186
     * </p>
 
187
     * 
 
188
     * <code><pre>
 
189
     * MagicNumberFileFilter tarFileFilter =
 
190
     *     MagicNumberFileFilter(new byte[] {0x75, 0x73, 0x74, 0x61, 0x72}, 257); 
 
191
     * </pre></code>
 
192
     * 
 
193
     * <code><pre>
 
194
     * MagicNumberFileFilter javaClassFileFilter =
 
195
     *     MagicNumberFileFilter(new byte[] {0xCA, 0xFE, 0xBA, 0xBE}, 0); 
 
196
     * </pre></code>
 
197
     * 
 
198
     * @param magicNumber the magic number to look for in the file.
 
199
     * @param offset the byte offset in the file to start comparing bytes.
 
200
     * 
 
201
     * @throws IllegalArgumentException if <code>magicNumber</code> is 
 
202
     *         {@code null}, or contains no bytes, or <code>offset</code> 
 
203
     *         is a negative number.
 
204
     */
 
205
    public MagicNumberFileFilter(byte[] magicNumber, long offset) {
 
206
        if (magicNumber == null) {
 
207
            throw new IllegalArgumentException("The magic number cannot be null");
 
208
        }
 
209
        if (magicNumber.length == 0) {
 
210
            throw new IllegalArgumentException("The magic number must contain at least one byte");
 
211
        }
 
212
        if (offset < 0) {
 
213
            throw new IllegalArgumentException("The offset cannot be negative");
 
214
        }
 
215
        
 
216
        this.magicNumbers = new byte[magicNumber.length];
 
217
        System.arraycopy(magicNumber, 0, this.magicNumbers, 0, magicNumber.length);
 
218
        this.byteOffset = offset;
 
219
    }
 
220
    
 
221
    /**
 
222
     * <p>
 
223
     * Accepts the provided file if the file contains the file filter's magic
 
224
     * number at the specified offset.
 
225
     * </p>
 
226
     * 
 
227
     * <p>
 
228
     * If any {@link IOException}s occur while reading the file, the file will
 
229
     * be rejected.
 
230
     * </p>
 
231
     * 
 
232
     * @param file the file to accept or reject.
 
233
     * 
 
234
     * @return {@code true} if the file contains the filter's magic number 
 
235
     *         at the specified offset, {@code false} otherwise.
 
236
     */
 
237
    @Override
 
238
    public boolean accept(File file) {
 
239
        if (file != null && file.isFile() && file.canRead()) {
 
240
            RandomAccessFile randomAccessFile = null;
 
241
            try {
 
242
                byte[] fileBytes = new byte[this.magicNumbers.length]; 
 
243
                randomAccessFile = new RandomAccessFile(file, "r");
 
244
                randomAccessFile.seek(byteOffset);
 
245
                int read = randomAccessFile.read(fileBytes);
 
246
                if (read != magicNumbers.length) {
 
247
                    return false;
 
248
                }
 
249
                return Arrays.equals(this.magicNumbers, fileBytes);
 
250
            } catch (IOException ioe) {
 
251
                // Do nothing, fall through and do not accept file
 
252
            } finally {
 
253
                IOUtils.closeQuietly(randomAccessFile);
 
254
            }
 
255
        }
 
256
        
 
257
        return false;
 
258
    }
 
259
 
 
260
    /**
 
261
     * Returns a String representation of the file filter, which includes the 
 
262
     * magic number bytes and byte offset.
 
263
     * 
 
264
     * @return a String representation of the file filter.
 
265
     */
 
266
    @Override
 
267
    public String toString() {
 
268
        StringBuilder builder = new StringBuilder(super.toString());
 
269
        builder.append("(");
 
270
        builder.append(new String(magicNumbers));// TODO perhaps use hex if value is not printable
 
271
        builder.append(",");
 
272
        builder.append(this.byteOffset);
 
273
        builder.append(")");
 
274
        return builder.toString();
 
275
    }
 
276
}