Package org.jnode.fs.spi

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

/*
* $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.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.jnode.fs.FSEntry;

/**
* A table containing all the entries of a directory. This class and its childs
* have the responsibility to identify an entry by its name (case sensitivity,
* long file name, ...). The class can limit the number of entries (for root
* directories ...) if necessary.
*
* @author Fabien DUMINY
*/
public class FSEntryTable extends AbstractFSObject {

    private static final Logger log = Logger.getLogger(FSEntryTable.class);

    /**
     * An empty table that's used as a default table (that can't be modified)
     * for FSDirectory.
     */
    public static final FSEntryTable EMPTY_TABLE = new FSEntryTable() {
        // FIXME ... actually it CAN be modified!!
    };

    /**
     * Map of entries (key=name, value=entry). As a value may be null (a free
     * entry) we must use Hashtable and not Hashtable
     */
    private Map<String, FSEntry> entries; // must be a HashMap

    /**
     * Map of entries (key=id, value=entry). As a value may be null (a free
     * entry) we must use Hashtable and not Hashtable
     */
    private Map<String, FSEntry> entriesById; // must be a HashMap

    /**
     * Names of the entries (list of String or null)
     */
    private List<String> entryNames;

    /**
     * Private constructor for EMPTY_TABLE
     */
    private FSEntryTable() {
        entries = Collections.emptyMap();
        entriesById = Collections.emptyMap();
        entryNames = Collections.emptyList();
    }

    /**
     * Construct a FSEntryTable from a list of FSEntry
     *
     * @param fs
     * @param entryList
     */
    public FSEntryTable(AbstractFileSystem<?> fs, List<FSEntry> entryList) {
        super(fs);
        // As a value may be null (a free entry)
        // we must use HashMap and not Hashtable
        this.entries = new HashMap<String, FSEntry>();
        this.entriesById = new HashMap<String, FSEntry>();
        this.entryNames = new ArrayList<String>();

        for (FSEntry entry : entryList) {
            if (entry == null) {
                entries.put(null, null);
                entryNames.add(null);
            } else {
                final String name = normalizeName(entry.getName());
                log.debug("FSEntryTable: adding entry " + name + " (length=+" + name.length() + ")");
                entries.put(name, entry);
                entriesById.put(entry.getId(), entry);
                entryNames.add(name);
            }
        }
    }

    protected int addEntry(FSEntry entry) {
        // grow the entry table
        if (entry == null) {
            entryNames.add(null);
        } else {
            entryNames.add(entry.getName());
        }
        return entryNames.size() - 1;
    }

    /**
     * Find the index of free entry. If not found, resize the table is possible.
     * If resize is impossible, an IOException is thrown.
     *
     * @param entry
     * @return the index of a free entry
     */
    protected int findFreeEntry(FSEntry entry) {
        int size = entryNames.size();
        int freeIndex = -1;
        for (int i = 0; i < size; i++) {
            String n = entryNames.get(i);
            if (n == null) {
                freeIndex = i;
            }
        }

        if (freeIndex < 0) {
            freeIndex = addEntry(null);
        }

        return freeIndex;
    }

    /**
     * Get the entry given by its index. The result can be null.
     *
     * @param index
     * @return the FSEntry at index
     */
    public final FSEntry get(int index) {
        return get(entryNames.get(index));
    }

    /**
     * Get the entry given by its name. The result can be null.
     *
     * @param name
     * @return the FSEntry with given name
     */
    public FSEntry get(String name) {
        // name can't be null (it's reserved for free entries)
        if (name == null)
            return null;

        name = normalizeName(name);
        log.debug("get(" + name + ")");
        return entries.get(name);
    }

    /**
     * Get the entry given by its ID. The result can be null.
     *
     * @param id the ID to lookup.
     * @return the FSEntry with given ID.
     */
    public FSEntry getById(String id) {
        if (id == null) {
            return null;
        }

        return entriesById.get(id);
    }

    /**
     * return a list of FSEntry names
     *
     * @return a List of FSEntry names
     */
    protected List<String> getEntryNames() {
        return entryNames;
    }

    /**
     * return a list of used FSEntry
     *
     * @return a list of used FSEntrys
     */
    protected List<FSEntry> getUsedEntries() {
        int nbEntries = entryNames.size();
        ArrayList<FSEntry> used = new ArrayList<FSEntry>(nbEntries / 2);

        for (int i = 0; i < nbEntries; i++) {
            String name = entryNames.get(i);
            if (name != null) {
                used.add(entries.get(name));
            }
        }

        return used;
    }

    /**
     * Get the index of an entry given byt its name. If there is no entry with
     * this name, return -1.
     *
     * @param name
     * @return the index of the given entry name
     */
    protected int indexOfEntry(String name) {
        return entryNames.indexOf(normalizeName(name));
    }

    /**
     * Indicate if the table need to be saved to the device.
     *
     * @return if the table needs to be saved to the device
     * @throws IOException
     */
    public final boolean isDirty() throws IOException {
        if (super.isDirty()) {
            return true;
        }

        for (FSEntry entry : entries.values()) {
            if (entry != null) {
                if (entry.isDirty()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Iterator that returns all used entries (ie entries that aren't null)
     *
     * @return an Iterator with all used entries
     */
    public final Iterator<FSEntry> iterator() {
        return new Iterator<FSEntry>() {
            private int index = 0;

            private List<FSEntry> usedEntries = getUsedEntries();

            public boolean hasNext() {
                return index < usedEntries.size();
            }

            public FSEntry next() {
                final FSEntry entry = usedEntries.get(index);
                index++;
                return entry;
            }

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

    /**
     * Return a normalized entry name (for case insensitivity for example)
     *
     * @param name
     * @return a normalized name (e.g. case insensitiv)
     */
    protected String normalizeName(String name) {
        return name;
    }

    /**
     * Remove an entry given by its name
     *
     * @param name
     * @return the index of removed entry
     */
    public int remove(String name) {
        name = normalizeName(name);
        int index = entryNames.indexOf(name);
        if (index < 0)
            return -1;

        FSEntry entry = entries.get(name);
        if (entry != null) {
            entriesById.remove(entry.getId());
        }

        // in entries and entryNames, a free (deleted) entry
        // is represented by null
        entries.put(name, null);

        entryNames.set(index, null);

        return index;
    }

    /**
     * Rename an entry given by its oldName.
     *
     * @param oldName
     * @param newName
     * @return the index of renamed file
     */
    public int rename(String oldName, String newName) {
        log.debug("<<< BEGIN rename oldName=" + oldName + " newName=" + newName + " >>>");
        log.debug("rename: table=" + toString());
        oldName = normalizeName(oldName);
        newName = normalizeName(newName);
        log.debug("rename oldName=" + oldName + " newName=" + newName);
        if (!entryNames.contains(oldName)) {
            log.debug("<<< END rename return false (oldName not found) >>>");
            return -1;
        }

        int index = entryNames.indexOf(oldName);
        if (index < 0)
            return -1;

        entryNames.set(index, newName);

        FSEntry entry = entries.remove(oldName);
        entries.put(newName, entry);

        log.debug("<<< END rename return true >>>");
        return index;
    }

    /**
     * Find a free entry in the table and set it with newEntry. If the table is
     * too small, it is resized. If the table can't be resized, an IOException
     * is thrown
     *
     * @param newEntry
     * @return the index of the stored entry
     * @throws IOException if directory is full (can't be resized)
     */
    public int setFreeEntry(FSEntry newEntry) throws IOException {
        String name = normalizeName(newEntry.getName());
        int index = findFreeEntry(newEntry);
        if (index < 0) {
            log.debug("setFreeEntry: ERROR: entry table is full");
            throw new IOException("Directory is full");
        }

        /* Object oldN = */
        entryNames.set(index, name);
        /* Object oldE = */
        entries.put(name, newEntry);
        entriesById.put(newEntry.getId(), newEntry);

        // entry added, so need to be flushed later
        setDirty();
        return index;
    }

    /**
     * Get the actual size of the table : the number of free entries + the
     * number of used entries.
     *
     * @return the complete size of the entry table
     */
    public final int size() {
        return entryNames.size();
    }

    /**
     * Return a list of FSEntry representing the content of the table.
     *
     * @return a list of all FSEntries
     */
    public List<?>/* <FSEntry> */toList() {
        // false means not compacted (ie can contain some null entries)
        return toList(false);
    }

    /**
     * Return a list of FSEntry representing the content of the table. The table
     * can be compacted (ie: without null entries) or uncompacted (with null
     * entries if there are).
     *
     * @param compacted
     * @return a list of FSEntries
     */
    public List<FSEntry> toList(boolean compacted) {
        ArrayList<FSEntry> entryList = new ArrayList<FSEntry>();

        int nbEntries = entryNames.size();
        for (int i = 0; i < nbEntries; i++) {
            FSEntry entry = get(i);
            if (!compacted || (compacted && (entry != null))) {
                entryList.add(entry);
            }
        }

        return entryList;
    }

    /**
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    public String toString() {
        int nbEntries = entryNames.size();
        String name;
        StringBuilder sb = new StringBuilder(nbEntries * 16);
        for (int i = 0; i < nbEntries; i++) {
            name = entryNames.get(i);
            sb.append("name:").append(name);
            sb.append("->entry:").append(entries.get(name));
            sb.append('\n');
        }

        return sb.toString();
    }
}
TOP

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

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.