Package org.jnode.fs.spi

Source Code of org.jnode.fs.spi.AbstractFSDirectory

/*
* $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.spi;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.jnode.fs.FSDirectory;
import org.jnode.fs.FSEntry;
import org.jnode.fs.ReadOnlyFileSystemException;

/**
* An abstract implementation of FSDirectory that contains common things among
* many FileSystems
*
* @author Fabien DUMINY
*/
public abstract class AbstractFSDirectory extends AbstractFSObject implements FSDirectory {
    private static final Logger log = Logger.getLogger(AbstractFSDirectory.class);

    /* Table of entries */
    private FSEntryTable entries = FSEntryTable.EMPTY_TABLE;

    /* Is this directory a root-directory? */
    private boolean isRoot;

    /**
     * Constructor for a new non-root directory
     *
     * @param fs
     */
    public AbstractFSDirectory(AbstractFileSystem<?> fs) {
        this(fs, false);
    }

    /**
     * Constructor for a new directory (root or non-root)
     *
     * @param fs
     * @param root true if it's a root directory
     */
    public AbstractFSDirectory(AbstractFileSystem<?> fs, boolean root) {
        super(fs);
        this.isRoot = root;
    }

    /**
     * Print the contents of this directory to the given writer. Used for
     * debugging purposes.
     *
     * @param out
     */
    public final void printTo(PrintWriter out) {
        checkEntriesLoaded();
        int freeCount = 0;
        int size = entries.size();
        for (int i = 0; i < size; i++) {
            FSEntry entry = entries.get(i);
            if (entry != null) {
                out.println("0x" + Integer.toHexString(i) + " " + entry);
            } else {
                freeCount++;
            }
        }
        out.println("Unused entries " + freeCount);
    }

    /**
     * @see org.jnode.fs.FSDirectory#iterator()
     */
    public final Iterator<FSEntry> iterator() throws IOException {
        checkEntriesLoaded();
        return entries.iterator();
    }

    /**
     * Is this directory a root ?
     *
     * @return if this directory is the root
     */
    public final boolean isRoot() {
        return isRoot;
    }

    /**
     * Gets the entry with the given name.
     *
     * @see org.jnode.fs.FSDirectory#getEntry(java.lang.String)
     */
    public final FSEntry getEntry(String name) throws IOException {
        // ensure entries are loaded from BlockDevice
        checkEntriesLoaded();

        return entries.get(name);
    }

    /**
     * Add a new directory with a given name
     *
     * @param name
     * @return the new added directory
     * @throws IOException
     */
    public final synchronized FSEntry addDirectory(String name) throws IOException {
        log.debug("<<< BEGIN addDirectory " + name + " >>>");
        if (!canWrite())
            throw new ReadOnlyFileSystemException("Filesystem or directory is mounted read-only!");

        if (getEntry(name) != null) {
            throw new IOException("File or Directory already exists" + name);
        }
        FSEntry newEntry = createDirectoryEntry(name);
        setFreeEntry(newEntry);
        log.debug("<<< END addDirectory " + name + " >>>");
        return newEntry;
    }

    /**
     * Remove a file or directory with a given name
     *
     * @param name
     * @throws IOException
     */
    public synchronized void remove(String name) throws IOException {
        if (!canWrite())
            throw new IOException("Filesystem or directory is mounted read-only!");
        if (entries.remove(name) >= 0) {
            setDirty();
            flush();
            return;
        } else
            throw new FileNotFoundException(name);
    }

    /**
     * Flush the contents of this directory to the persistent storage
     *
     * @throws IOException
     */
    public final void flush() throws IOException {
        if (canWrite()) {
            boolean flushEntries = isEntriesLoaded() && entries.isDirty();
            if (isDirty() || flushEntries) {
                writeEntries(entries);
                entries.resetDirty();
                resetDirty();
            }
        }
    }

    /**
     * Read the entries of this directory from the persistent storage
     *
     * @return a list of entries for this directory
     * @throws IOException
     */
    protected abstract FSEntryTable readEntries() throws IOException;

    /**
     * Write the entries of this directory to the persistent storage
     *
     * @param entries a list of entries for this directory
     * @throws IOException
     */
    protected abstract void writeEntries(FSEntryTable entries) throws IOException;

    /**
     * Is this directory dirty (ie is there any data to save to device) ?
     *
     * @return if this directory is dirty
     * @throws IOException
     */
    public final boolean isDirty() throws IOException {
        if (super.isDirty()) {
            return true;
        }
        // If entries are not loaded, they are clean (ie: in the storage) !
        if (isEntriesLoaded() && entries.isDirty())
            return true;
        return false;
    }

    /**
     * BE CAREFULL : don't call this method from the constructor of this class
     * because it call the method readEntries of the child classes that are not
     * yet initialized (constructed).
     */
    protected final void checkEntriesLoaded() {
        log.debug("<<< BEGIN checkEntriesLoaded >>>");
        if (!isEntriesLoaded()) {
            log.debug("checkEntriesLoaded : loading");
            try {
                if (canRead()) {
                    entries = readEntries();
                } else {
                    // the next time, we will call checkEntriesLoaded()
                    // we will retry to load entries
                    entries = FSEntryTable.EMPTY_TABLE;
                    log.debug("checkEntriesLoaded : can't read, using EMPTY_TABLE");
                }
                resetDirty();
            } catch (IOException e) {
                log.fatal("unable to read directory entries", e);
                // the next time, we will call checkEntriesLoaded()
                // we will retry to load entries
                entries = FSEntryTable.EMPTY_TABLE;
            }
        }
        log.debug("<<< END checkEntriesLoaded >>>");
    }

    /**
     * Have we already loaded our entries from device ?
     *
     * @return if the entries are allready loaded from the device
     */
    private final boolean isEntriesLoaded() {
        return (entries != FSEntryTable.EMPTY_TABLE);
    }

    /**
     * Add a new file with a given name
     *
     * @param name
     * @throws IOException
     * @return the added file entry
     */
    public final synchronized FSEntry addFile(String name) throws IOException {
        log.debug("<<< BEGIN addFile " + name + " >>>");
        if (!canWrite())
            throw new ReadOnlyFileSystemException("Filesystem or directory is mounted read-only!");

        if (getEntry(name) != null) {
            throw new IOException("File or directory already exists: " + name);
        }
        FSEntry newEntry = createFileEntry(name);
        setFreeEntry(newEntry);

        log.debug("<<< END addFile " + name + " >>>");
        return newEntry;
    }

    /**
     * Abstract method to create a new file entry from the given name
     *
     * @param name
     * @return the new created file
     * @throws IOException
     */
    protected abstract FSEntry createFileEntry(String name) throws IOException;

    /**
     * Abstract method to create a new directory entry from the given name
     * @param name
     * @return the new created directory
     * @throws IOException
     */
    protected abstract FSEntry createDirectoryEntry(String name) throws IOException;

    /**
     * Find a free entry and set it with the given entry
     * @param newEntry
     * @throws IOException
     */
    private final void setFreeEntry(FSEntry newEntry) throws IOException {
        checkEntriesLoaded();
        if (entries.setFreeEntry(newEntry) >= 0) {
            log.debug("setFreeEntry: free entry found !");
            // a free entry has been found
            setDirty();
            flush();
            return;
        }
    }

    /**
     * Return our entry table
     * @return the entry table
     */
    protected FSEntryTable getEntryTable() {
        return entries;
    }

    /**
     * @return a string representation of this instance.
     */
    public String toString() {
        return entries.toString();
    }
}
TOP

Related Classes of org.jnode.fs.spi.AbstractFSDirectory

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.