Package org.jnode.fs.fat

Source Code of org.jnode.fs.fat.FatLfnDirectory

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.fs.fat;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import org.jnode.fs.FSEntry;
import org.jnode.fs.ReadOnlyFileSystemException;

/**
* @author gbin
*/
public class FatLfnDirectory extends FatDirectory {
    private HashMap<String, LfnEntry> shortNameIndex = new HashMap<String, LfnEntry>();
    private HashMap<String, LfnEntry> longFileNameIndex = new HashMap<String, LfnEntry>();

    /**
     * @param fs
     * @param file
     * @throws IOException
     */
    public FatLfnDirectory(FatFileSystem fs, FatFile file) throws IOException {
        super(fs, file);
    }

    // for root
    public FatLfnDirectory(FatFileSystem fs, int nrEntries) {
        super(fs, nrEntries);
    }

    public FSEntry addFile(String name) throws IOException {
        if (getFileSystem().isReadOnly()) {
            throw new ReadOnlyFileSystemException("addFile in readonly filesystem");
        }

        name = name.trim();
        String shortName = generateShortNameFor(name);
        FatDirEntry realEntry = new FatDirEntry(this, splitName(shortName), splitExt(shortName));
        LfnEntry entry = new LfnEntry(this, realEntry, name);
        shortNameIndex.put(shortName, entry);
        longFileNameIndex.put(name, entry);
        setDirty();
        flush();
        return entry;
    }

    public FSEntry addDirectory(String name) throws IOException {
        if (getFileSystem().isReadOnly()) {
            throw new ReadOnlyFileSystemException("addDirectory in readonly filesystem");
        }

        name = name.trim();
        String shortName = generateShortNameFor(name);
        FatDirEntry realEntry = new FatDirEntry(this, splitName(shortName), splitExt(shortName));

        final long parentCluster;
        if (file == null) {
            parentCluster = 0;
        } else {
            parentCluster = file.getStartCluster();
        }

        final int clusterSize = getFatFileSystem().getClusterSize();
        realEntry.setFlags(FatConstants.F_DIRECTORY);
        final FatFile file = realEntry.getFatFile();
        file.setLength(clusterSize);

        // TODO optimize it also to use ByteBuffer at lower level
        // final byte[] buf = new byte[clusterSize];
        final ByteBuffer buf = ByteBuffer.allocate(clusterSize);

        // Clean the contents of this cluster to avoid reading strange data
        // in the directory.
        // file.write(0, buf, 0, buf.length);
        file.write(0, buf);

        file.getDirectory().initialize(file.getStartCluster(), parentCluster);

        LfnEntry entry = new LfnEntry(this, realEntry, name);
        shortNameIndex.put(shortName, entry);
        longFileNameIndex.put(name, entry);
        setDirty();
        flush();
        return entry;
    }

    public FSEntry getEntry(String name) {
        // System.out.println("Search : " + name);
        name = name.trim();
        FSEntry entry;
        // try first as a long file name
        entry = longFileNameIndex.get(name);
        if (entry == null)
            return shortNameIndex.get(name.toUpperCase());
        else
            return entry;

    }

    protected synchronized void read(byte[] src) {
        super.read(src);
        readLFN();
    }

    private void readLFN() {
        // System.out.println("Read LFN");
        int i = 0;
        int size = entries.size();

        while (i < size) {
            // jump over empty entries
            while (i < size && entries.get(i) == null) {
                i++;
            }

            if (i >= size) {
                break;
            }

            int offset = i; // beginning of the entry
            // check when we reach a real entry
            while (entries.get(i) instanceof FatLfnDirEntry) {
                // System.out.println(" Jumped over : " + entries.get(i));
                i++;
                if (i >= size) {
                    // This is a cutted entry, forgive it
                    break;
                }
            }
            i++;
            if (i >= size) {
                // This is a cutted entry, forgive it
                break;
            }

            LfnEntry current = new LfnEntry(this, entries, offset, i - offset);

            if (!current.isDeleted() && current.isValid()) {
                shortNameIndex.put(current.getRealEntry().getName(), current);
                longFileNameIndex.put(current.getName(), current);
            }
        }

    }

    private void updateLFN() throws IOException {
        Vector<FatBasicDirEntry> destination = new Vector<FatBasicDirEntry>();

        for (LfnEntry currentEntry : shortNameIndex.values()) {
            FatBasicDirEntry[] encoded = currentEntry.compactForm();
            Collections.addAll(destination, encoded);
        }

        final int size = destination.size();
        if (entries.size() < size) {
            if (!canChangeSize(size)) {
                throw new IOException("Directory is full");
            }
        }

        boolean useAdd = false;
        for (int i = 0; i < size; i++) {
            if (!useAdd) {
                try {
                    entries.set(i, destination.get(i));
                } catch (ArrayIndexOutOfBoundsException aEx) {
                    useAdd = true;
                }
            }
            if (useAdd) {
                entries.add(i, destination.get(i));
            }
        }

        final int entireSize = entries.size();
        for (int i = size; i < entireSize; i++) {
            entries.set(i, null); // remove stale entries
        }

    }

    public void flush() throws IOException {
        updateLFN();
        super.flush();
    }

    public Iterator<FSEntry> iterator() {
        return new Iterator<FSEntry>() {
            Iterator<LfnEntry> it = shortNameIndex.values().iterator();

            public boolean hasNext() {
                return it.hasNext();
            }

            public FSEntry next() {
                return it.next();
            }

            /**
             * @see java.util.Iterator#remove()
             */
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    /*
     * Its in the DOS manual!(DOS 5: page 72) Valid: A..Z 0..9 _ ^ $ ~ ! # % & - {} () @ ' `
     *
     * Invalid: spaces/periods,
     */

    public String generateShortNameFor(String longFullName) {
        int dotIndex = longFullName.lastIndexOf('.');

        String longName;
        String longExt;

        if (dotIndex == -1) {
            // No dot in the name
            longName = longFullName;
            longExt = ""; // so no extension
        } else {
            // split it at the dot
            longName = longFullName.substring(0, dotIndex);
            longExt = longFullName.substring(dotIndex + 1);
        }

        String shortName = longName;
        String shortExt = longExt;

        // make the extension short
        if (shortExt.length() > 3) {
            shortExt = shortExt.substring(0, 3);
        }

        // make the ~n name short
        if (shortName.length() > 8) {
            // trim it
            char[] shortNameChar = shortName.substring(0, 7).toUpperCase().toCharArray();

            // epurate it from alien characters
        loop:
            for (int i = 0; i < shortNameChar.length; i++) {
                char toTest = shortNameChar[i];
            valid:
                {
                    if (toTest > 255)
                        break valid;
                    if (toTest == ' ')
                        break valid;
                    if (toTest >= 'A' && toTest <= 'Z')
                        continue loop;
                    if (toTest >= '0' && toTest <= '9')
                        continue loop;
                    if (toTest == '_' || toTest == '^' || toTest == '$' || toTest == '~' ||
                        toTest == '!' || toTest == '#' || toTest == '%' || toTest == '&' ||
                        toTest == '-' || toTest == '{' || toTest == '}' || toTest == '(' ||
                        toTest == ')' || toTest == '@' || toTest == '\'' || toTest == '`')
                        continue loop;

                }
                shortNameChar[i] = '_';

            }

            // name range from "nnnnnn~1" to "~9999999"
            for (int i = 1; i <= 99999999; i++) {
                String tildeStuff = "~" + i;
                int tildeStuffLength = tildeStuff.length();
                System.arraycopy(tildeStuff.toCharArray(), 0, shortNameChar, 7 - tildeStuffLength,
                    tildeStuffLength);
                shortName = new String(shortNameChar);
                if (!shortNameIndex.containsKey(shortName + "." + shortExt))
                    break;
            }
        }

        String shortFullName = shortName + "." + shortExt;
        return shortFullName.toUpperCase();
    }

    /**
     * Remove the entry with the given name from this directory.
     *
     * @param name
     * @throws IOException
     */
    public void remove(String name) throws IOException {
        name = name.trim();
        LfnEntry byLongName = longFileNameIndex.get(name);

        if (byLongName != null) {
            longFileNameIndex.remove(name);
            shortNameIndex.remove(byLongName.getRealEntry().getName());
            return;
        }

        String uppedName = name.toUpperCase();
        LfnEntry byShortName = shortNameIndex.get(uppedName);

        if (byShortName != null) {
            longFileNameIndex.remove(byShortName.getName());
            shortNameIndex.remove(uppedName);
        }
        throw new FileNotFoundException(name);
    }

}
TOP

Related Classes of org.jnode.fs.fat.FatLfnDirectory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.