Home | History | Annotate | Download | only in fat
      1 /*
      2  * Copyright (C) 2009,2010 Matthias Treydte <mt (at) waldheinz.de>
      3  *
      4  * This library is free software; you can redistribute it and/or modify it
      5  * under the terms of the GNU Lesser General Public License as published
      6  * by the Free Software Foundation; either version 2.1 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
     12  * License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this library; If not, write to the Free Software Foundation, Inc.,
     16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     17  */
     18 
     19 package de.waldheinz.fs.fat;
     20 
     21 import java.math.BigInteger;
     22 import java.util.Arrays;
     23 
     24 /**
     25  * Represents a "short" (8.3) file name as used by DOS.
     26  *
     27  * @author Matthias Treydte &lt;waldheinz at gmail.com&gt;
     28  */
     29 final class ShortName {
     30 
     31     /**
     32      * These are taken from the FAT specification.
     33      */
     34     private final static byte[] ILLEGAL_CHARS = {
     35             0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B,
     36             0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C
     37     };
     38 
     39     /**
     40      * The name of the "current directory" (".") entry of a FAT directory.
     41      */
     42     public final static ShortName DOT = new ShortName(".", ""); // NOI18N
     43 
     44     /**
     45      * The name of the "parent directory" ("..") entry of a FAT directory.
     46      */
     47     public final static ShortName DOT_DOT = new ShortName("..", ""); // NOI18N
     48 
     49     private final char[] name;
     50     private boolean mShortNameOnly;
     51 
     52     private ShortName(String nameExt) {
     53         if (nameExt.length() > 12)
     54             throw new IllegalArgumentException("name too long");
     55 
     56         final int i = nameExt.indexOf('.');
     57         final String nameString, extString;
     58 
     59         if (i < 0) {
     60             nameString = nameExt.toUpperCase();
     61             extString = "";
     62         } else {
     63             nameString = nameExt.substring(0, i).toUpperCase();
     64             extString = nameExt.substring(i + 1).toUpperCase();
     65         }
     66 
     67         this.name = toCharArray(nameString, extString);
     68         checkValidChars(this.name);
     69     }
     70 
     71     ShortName(String name, String ext) {
     72         this.name = toCharArray(name, ext);
     73     }
     74 
     75     ShortName(char[] name) {
     76         this.name = name;
     77     }
     78 
     79     public ShortName(char[] nameArr, char[] extArr) {
     80         char[] result = new char[11];
     81         System.arraycopy(nameArr, 0, result, 0, nameArr.length);
     82         System.arraycopy(extArr, 0, result, 8, extArr.length);
     83         this.name = result;
     84     }
     85 
     86     private static char[] toCharArray(String name, String ext) {
     87         checkValidName(name);
     88         checkValidExt(ext);
     89 
     90         final char[] result = new char[11];
     91         Arrays.fill(result, ' ');
     92         System.arraycopy(name.toCharArray(), 0, result, 0, name.length());
     93         System.arraycopy(ext.toCharArray(), 0, result, 8, ext.length());
     94 
     95         return result;
     96     }
     97 
     98     /**
     99      * Calculates the checksum that is used to test a long file name for it's
    100      * validity.
    101      *
    102      * @return the {@code ShortName}'s checksum
    103      */
    104     public byte checkSum() {
    105         final byte[] dest = new byte[11];
    106         for (int i = 0; i < 11; i++)
    107             dest[i] = (byte) name[i];
    108 
    109         int sum = dest[0];
    110         for (int i = 1; i < 11; i++) {
    111             sum = dest[i] + (((sum & 1) << 7) + ((sum & 0xfe) >> 1));
    112         }
    113 
    114         return (byte) (sum & 0xff);
    115     }
    116 
    117     /**
    118      * Parses the specified string into a {@code ShortName}.
    119      *
    120      * @param name the name+extension of the {@code ShortName} to get
    121      * @return the {@code ShortName} representing the specified name
    122      * @throws IllegalArgumentException if the specified name can not be parsed
    123      *             into a {@code ShortName}
    124      * @see #canConvert(java.lang.String)
    125      */
    126     public static ShortName get(String name) throws IllegalArgumentException {
    127         if (name.equals("."))
    128             return DOT;
    129         else if (name.equals(".."))
    130             return DOT_DOT;
    131         else
    132             return new ShortName(name);
    133     }
    134 
    135     /**
    136      * Tests if the specified string can be converted to a {@code ShortName}.
    137      *
    138      * @param nameExt the string to test
    139      * @return if the string can be converted
    140      * @see #get(java.lang.String)
    141      */
    142     public static boolean canConvert(String nameExt) {
    143         /* TODO: do this without exceptions */
    144         try {
    145             ShortName.get(nameExt);
    146             return true;
    147         } catch (IllegalArgumentException ex) {
    148             return false;
    149         }
    150     }
    151 
    152     public static ShortName parse(byte[] data) {
    153         final char[] nameArr = new char[8];
    154 
    155         for (int i = 0; i < nameArr.length; i++) {
    156             nameArr[i] = (char) LittleEndian.getUInt8(data, i);
    157         }
    158 
    159         final char[] extArr = new char[3];
    160         for (int i = 0; i < extArr.length; i++) {
    161             extArr[i] = (char) LittleEndian.getUInt8(data, 0x08 + i);
    162         }
    163 
    164         return new ShortName(nameArr, extArr);
    165     }
    166 
    167     public void write(byte[] dest) {
    168         for (int i = 0; i < 11; i++) {
    169             dest[i] = (byte) name[i];
    170         }
    171     }
    172 
    173     public String asSimpleString() {
    174         return new String(this.name).trim();
    175     }
    176 
    177     @Override
    178     public String toString() {
    179         StringBuilder sb = new StringBuilder();
    180         for (int i = 0; i < this.name.length; i++) {
    181             sb.append(Integer.toHexString(name[i]));
    182             sb.append(' ');
    183         }
    184         return getClass().getSimpleName() +
    185                 " [" +
    186                 asSimpleString() + " -- " +
    187                 sb.toString() + "]"; // NOI18N
    188     }
    189 
    190     private static void checkValidName(String name) {
    191         checkString(name, "name", 1, 8);
    192     }
    193 
    194     private static void checkValidExt(String ext) {
    195         checkString(ext, "extension", 0, 3);
    196     }
    197 
    198     private static void checkString(String str, String strType,
    199             int minLength, int maxLength) {
    200 
    201         if (str == null)
    202             throw new IllegalArgumentException(strType +
    203                     " is null");
    204         if (str.length() < minLength)
    205             throw new IllegalArgumentException(strType +
    206                     " must have at least " + minLength +
    207                     " characters: " + str);
    208         if (str.length() > maxLength)
    209             throw new IllegalArgumentException(strType +
    210                     " has more than " + maxLength +
    211                     " characters: " + str);
    212     }
    213 
    214     @Override
    215     public boolean equals(Object obj) {
    216         if (!(obj instanceof ShortName)) {
    217             return false;
    218         }
    219 
    220         final ShortName other = (ShortName) obj;
    221         return Arrays.equals(name, other.name);
    222     }
    223 
    224     @Override
    225     public int hashCode() {
    226         return Arrays.hashCode(this.name);
    227     }
    228 
    229     public void setHasShortNameOnly(boolean hasShortNameOnly) {
    230         mShortNameOnly = hasShortNameOnly;
    231     }
    232 
    233     public boolean hasShortNameOnly() {
    234         return mShortNameOnly;
    235     }
    236 
    237     /**
    238      * Checks if the specified char array consists only of "valid" byte values
    239      * according to the FAT specification.
    240      *
    241      * @param chars the char array to test
    242      * @throws IllegalArgumentException if invalid chars are contained
    243      */
    244     public static void checkValidChars(char[] chars)
    245             throws IllegalArgumentException {
    246 
    247         if (chars[0] == 0x20)
    248             throw new IllegalArgumentException(
    249                     "0x20 can not be the first character");
    250 
    251         for (int i = 0; i < chars.length; i++) {
    252             if ((chars[i] & 0xff) != chars[i])
    253                 throw new IllegalArgumentException("multi-byte character at " + i);
    254 
    255             final byte toTest = (byte) (chars[i] & 0xff);
    256 
    257             if (toTest < 0x20 && toTest != 0x05)
    258                 throw new IllegalArgumentException("character < 0x20 at" + i);
    259 
    260             for (int j = 0; j < ILLEGAL_CHARS.length; j++) {
    261                 if (toTest == ILLEGAL_CHARS[j])
    262                     throw new IllegalArgumentException("illegal character " +
    263                             ILLEGAL_CHARS[j] + " at " + i);
    264             }
    265         }
    266     }
    267 }
    268