/* * Copyright 2003-2010 Tufts University Licensed under the * Educational Community License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.osedu.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ /* * Cabinet.java * * Created on September 17, 2003, 10:04 AM * * The software contained in this file is copyright 2003 by Mark J. Norton, all rights reserved. */ package tufts.oki.localFiling; import java.io.*; import java.util.*; import tufts.oki.shared.*; import osid.OsidException; /** * LocalCabinet corresponds to a directory in a local filing system. Root cabinets are * treated specially in that they have a rootBase. The rootBase is the path to the root * of the filing system. Usually this is empty or contains the drive letter, if a PC. * * @author Mark Norton * @author Scott Fraize * */ public class LocalCabinet extends LocalCabinetEntry implements osid.filing.Cabinet { private static final boolean UseCache = false; private static final Map<String,LocalCabinet> Cache; static { Cache = UseCache ? new java.util.concurrent.ConcurrentHashMap() : null; } //private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(LocalCabinet.class); /* parent is inherited from Cabinet Entry. */ private final SortedSet children; //private tufts.oki.shared.Properties properties = null; private volatile boolean initialized = false; // True if expanded to include entries. private boolean open = false; // Indicates open or closed status for UI. private final File dir; // The local directory being modeled. private final String rootBase; // Set if this is a root. private final String _cacheKey; public static LocalCabinet instance(String path, osid.shared.Agent owner, osid.filing.Cabinet parent) { if (UseCache) { LocalCabinet cabinet = Cache.get(path); if (cabinet == null) { cabinet = new LocalCabinet(path, owner, parent); Cache.put(path, cabinet); } else { if (Log.isDebugEnabled()) Log.debug("instance cache: " + cabinet); } return cabinet; } else return new LocalCabinet(path, owner, parent); } public static LocalCabinet instance(File file, osid.shared.Agent owner, osid.filing.Cabinet parent) { return instance(file.getPath(), owner, parent); } public String toString() { String name = "<unknown>"; try { name = getDisplayName(); } catch (Throwable t) { name = t.toString(); } if (rootBase == null) { return String.format("LocalCabinet@%07x[%s](%s) n=%02d", System.identityHashCode(this), _cacheKey, name, children == null ? -1 : children.size() ); } else { return String.format("LocalCabinet@%07x{%s} n=%02d", System.identityHashCode(this), rootBase, children == null ? -1 : children.size() ); } } private void debug(String fmt, Object... args) { if (Log.isDebugEnabled()) Log.debug(this + ": " + String.format(fmt, args)); } /** * Initializes the feilds interited from CabinetEntry and adds a vector of children. * Creates a Properties object to hold properties associated with this cabinet. * * @author Mark Norton * */ public LocalCabinet(String displayName, osid.shared.Agent agentOwner, osid.filing.Cabinet parent){ super (displayName, agentOwner, parent); _cacheKey = displayName; children = new TreeSet(new LocalCabinetEntryComparator()); //FilingCabinetType type = new FilingCabinetType(); //properties = new Properties(type); // Create a File for this cabinet. Treat roots specially. dir = new File (displayName); //System.out.println ("LocalCabinet creator - display name: " + dir.getName()); if (parent == null) { rootBase = dir.getPath(); updateDisplayName (rootBase); } else { updateDisplayName(dir.getName()); rootBase = null; } if (tufts.vue.DEBUG.IO && Log.isDebugEnabled()) { if (parent != null) Log.debug("CREATED in " + parent + ": " + this); else Log.debug("CREATED " + this); } } @Override public final boolean isCabinet() { return true; } /** * Add the entry to the list of children. * * @author Mark Norton */ public void add(osid.filing.CabinetEntry entry, java.lang.String name) throws osid.filing.FilingException { //System.out.println ("add - new display name: " + name); /* Update the display name in entry with name. Does this make sense? */ entry.updateDisplayName (name); /* Add the element to the Vector array. */ //Log.debug(this + " add " + entry + " [" + name + "]"); children.add(entry); } /** * Add the entry to the list of children. * * @author Mark Norton */ public void add(osid.filing.CabinetEntry entry) throws osid.filing.FilingException { /* Add the element to the Vector array. */ children.add(entry); } /* * Prints a list of the cabinet entries to stdout. Largely used for debugging purposes. * This method is not part of the OSID interface definition for Cabinet. * * @author Mark Norton */ public void list () throws osid.filing.FilingException { if (children == null) { System.out.println ("The Cabinet is empty."); } int len = children.size(); // for (int i = 0; i < len; i++) { Iterator i = children.iterator(); while(i.hasNext()) { LocalCabinetEntry entry = (LocalCabinetEntry) i.next(); if (entry instanceof LocalCabinet) System.out.println ("Cabinet " + i + ": " + entry.getDisplayName()); else if (entry instanceof LocalByteStore) System.out.println ("Byte Store " + i + ": " + entry.getDisplayName()); } // } } /** * Create a new ByteStore, add it to this Cabinet. * * @author Mark Norton * * @return A new ByteStore with the name provided and this cabinet as parent. */ public osid.filing.ByteStore createByteStore(String name) { //System.out.println ("createByteStore - name: " + name); osid.filing.ByteStore bs = null; try { bs = new LocalByteStore(name, this); //if (Log.isDebugEnabled()) Log.debug("CREATED " + bs); this.add (bs); } catch (osid.OsidException ex) { } return bs; } /* * The oldByteStore is copied into the new ByteStore. * * @author Mark Norton * * @return A new ByteStore with the name provided and this cabinet as parent. */ public osid.filing.ByteStore copyByteStore(String name, osid.filing.ByteStore oldByteStore) throws osid.filing.FilingException { osid.filing.ByteStore bs = (osid.filing.ByteStore) createByteStore (name); byte[] byte_store = ((LocalByteStore)oldByteStore).getBytes(); byte[] new_store = (byte[])byte_store.clone(); bs.write (new_store); return bs; } /** * Currently unimplemented. A lot depends on the eventual use of the filing * system implemented by these classes. Not all systems will have a restriction * on available and used byes. * * @author Mark Norton * * @return The number of bytes available in this cabinet. */ public long getAvailableBytes() throws osid.filing.FilingException { throw new osid.filing.FilingException (osid.filing.FilingException.UNIMPLEMENTED); } /** * Currently unimplemented. A lot depends on the eventual use of the filing * system implemented by these classes. Not all systems will have a restriction * on available and used byes. * <p> * This could be implemented as the sum of of the sizes of the ByteStores in this * directory, but its not clear what use that would be. * * @author Mark Norton * * @return The number of bytes used in this cabinet. */ public long getUsedBytes() throws osid.filing.FilingException { throw new osid.filing.FilingException (osid.filing.FilingException.UNIMPLEMENTED); } /** * Create a new cabinet entry with * the agentOwner of this new cabinet as this owner of this Cabinet. * * @author Mark Norton * * @return A new cabinet with the given displayName. */ public osid.filing.Cabinet createCabinet(String displayName) throws osid.filing.FilingException { //System.out.println ("createCabinet - name: " + displayName); try { osid.shared.Agent agentOwner = super.getCabinetEntryAgent(); LocalCabinet entry = instance(displayName, agentOwner, this); //LocalCabinet entry = new LocalCabinet(displayName, agentOwner, this); //entry.updateDisplayName(displayName); /* Add the element to the Vector array. */ children.add(entry); return (osid.filing.Cabinet) entry; } catch (OsidException e1) { throw new osid.filing.FilingException (osid.filing.FilingException.OPERATION_FAILED); //throw e1; } } /** * Creates an iterator which lists the entries in this cabinet. A check is made to * see if this cabinet was previously opened. If not, it is opened and initialized * with the entries contained in it. * * @author Mark Norton * * @return Return an iterator for the entries in this cabinet. */ public synchronized osid.filing.CabinetEntryIterator entries() throws osid.filing.FilingException { if (!initialized) loadChildren(); return new LocalCabinetEntryIterator(children); } private void loadChildren() throws osid.filing.FilingException { //tufts.Util.printStackTrace("ENTRIES " + this + "; " + getDisplayName()); debug("initializing"); // Initialize the directory by getting all entries contained in it. String[] files = null; //System.out.println ("Open Directory: " + this.cwd.getDisplayName()); files = dir.list(); if (files == null) throw new osid.filing.FilingException(osid.filing.FilingException.NOT_A_CABINET + ": no files"); debug("listed, n=%d", files.length); String rootBase = getRootBase(); String path = rootBase + getFullName(); //System.out.println ("openDirectory - path name: " + path); for (int i = 0; i < files.length; i++) { //File temp = new File (cwd.getPath(), files[i]); File temp = new File (rootBase+getFullName(), files[i]); //System.out.println ("openDirectory - new file: " + rootBase + getFullName() + files[i]); String absolute = null; if (isRootCabinet()) absolute = path + temp.getName(); else absolute = path + separator() + temp.getName(); if (temp.isDirectory()) { //System.out.println ("\tDir " + i + ": " + temp.getName() + "\t" + absolute); //cwd.createCabinet (temp.getName()); createCabinet (absolute); } else if (temp.isFile()) { //System.out.println ("\tFile " + i + ": " + temp.getName() + "\t" + absolute); //cwd.createByteStore (temp.getName()); createByteStore (absolute); } // Unknown cases are ignored. } // Set flags. open = true; initialized = true; debug("initialized"); //new Throwable("HERE").printStackTrace(); } /** * Get a cabinet entry given its name. * * @author Mark Norton * * @return The cabinet entry with the desired display name. Throws ITEM_DOES_NOT_EXIST if name is unknown. */ public osid.filing.CabinetEntry getCabinetEntryByName(String name) throws osid.filing.FilingException { Iterator i = children.iterator(); while(i.hasNext()) { // for (int i = 0; i < children.size(); i++) { LocalCabinetEntry entry = (LocalCabinetEntry) i.next(); //System.out.println ("getCabinetEntryByName - scan: " + entry.getDisplayName()); if (name.compareTo (entry.getDisplayName()) == 0) { return (osid.filing.CabinetEntry) entry; } } /* ITEM_DOES_NOT_EXIST is not exactly the right sentiment for not finding the entry. */ throw new osid.filing.FilingException (osid.filing.FilingException.ITEM_DOES_NOT_EXIST); } /** * Get a cabinet entry given its identifier. Throws ITEM_DOES_NOT_EXIST if Id is unknown. * * @author Mark Norton * * @return The cabinet entry corresponding to the identifier passed. */ public osid.filing.CabinetEntry getCabinetEntryById(osid.shared.Id id) throws osid.filing.FilingException { Iterator i = children.iterator(); while(i.hasNext()) { // for (int i = 0; i < children.size(); i++) { LocalCabinetEntry entry = (LocalCabinetEntry) i.next(); try { if (id.isEqual (entry.getId())) { return (osid.filing.CabinetEntry) entry; } } catch (osid.shared.SharedException ex) { /* Not exactly sure what could go wrong with an Id comparison, * but the compiler insists on catching this exception. * This will fall through to ITEM_DOES_NOT_EXIST exception throw. */ } } /* ITEM_DOES_NOT_EXIST is not exactly the right sentiment for not finding the entry. */ throw new osid.filing.FilingException (osid.filing.FilingException.ITEM_DOES_NOT_EXIST); } /** * Return the properties associated with this cabinet as a HashMap. This is better * implmented using the expanded Properties objects defined elsewhere. * * @author Mark Norton * * @return The property set of the Properties object associated with this cabinet as a Map. */ public java.util.Map getProperties() throws osid.filing.FilingException { throw new UnsupportedOperationException(getClass() + ".getProperties"); // // Properties found unsupported (uninitiailized) -- would throw NPE -- SMF 2007-10-10 // HashMap map = properties.getPropertySet(); // return (Map) map; } /** * Get the root cabinet of this cabinet. * * @author Mark Norton * * @return The root cabinet of this cabinet by searching up the parent links. */ public osid.filing.Cabinet getRootCabinet() { LocalCabinet cab = this; while (cab.getParent() != null) cab = (LocalCabinet)cab.getParent(); return cab; } /** * If this cabinet is a root, return rootBase. Otherwise find the root * and return rootBase. * * @author Mark Norton */ public String getRootBase() { if (super.getParent() == null) return rootBase; else { LocalCabinet root = (LocalCabinet) getRootCabinet(); return root.getRootBase(); } } /** * Currently, this always returns true, since cabinets are always defined to be * listable. This could be made a property instead. * * @author Mark Norton * * @return True if this cabinet is listable. */ public boolean isListable() throws osid.filing.FilingException { return true; } /** * Currently, this always returns true, since cabinets are defined to be managable * in this implementation. It could be made a property instead. * * @author Mark Norton * * @return True if this cabinet is managable. */ public boolean isManageable() throws osid.filing.FilingException { return true; } /** * Return true if this is a root cabinet, ie., its parent is null. * * @author Mark Norton * * @return True if this is a root cabinet (parent == null). */ public boolean isRootCabinet() throws osid.filing.FilingException { return (this.getParent() == null); } /** * Remove the entry indicated from the children of this cabinet. The entry is * identified by comparing the Ids of the entry passed to the Id of the children * present. This assumes that such Ids are globally unique. * <p> * No error is thrown if entry is not present. * * @author Mark Norton */ public void remove(osid.filing.CabinetEntry entry) throws osid.filing.FilingException { osid.shared.Id entry_id = entry.getId(); Iterator i = children.iterator(); while(i.hasNext()) { // for (int i = 0; i < children.size(); i++) { LocalCabinetEntry ent = (LocalCabinetEntry)i.next(); try { if (entry_id.isEqual (ent.getId())) { children.remove (entry); } } catch (osid.shared.SharedException ex) { /* Unlikely that isEqual() will throw a SharedException. */ } } } /** * Check to see if the cabinet is empty. This is largely used in remove and delete operations. * Note: This method is not part of the osid.filing interface definitions. * * @author Mark Norton * * @return True if this cabinet has no children of any kind. */ public boolean isEmpty() { if (children == null) return true; if (children.size() == 0) return true; else return false; } /** * Return the number of children this cabinent has. * <p> * This is an extension to osid.filing.Cabinet to support classes which imlement * javax.swing.tree.TreeModel. * * @author Mark Norton * * @return the number of children in this cabinet. */ public int getChildCount () { return children.size(); } /** * Check to see if this cabinet is open. * * @author Mark Norton * * @return true if the cabinet is open. */ public boolean isOpen () { return this.open; } /** * Set the open flag to the value given. * * @author Mark Norton */ public void setOpen (boolean flag) { this.open = flag; } /** * Check to see if this cabinet is initialized. * * @author Mark Norton * * @return true if the cabinet is initialized. */ public boolean isInitialized() { return this.initialized; } /** * Set the initialized flag to the value given. * * @author Mark Norton */ public void setInitialized(boolean flag) { this.initialized = flag; } /** * Get the file path to this cabinet entry. The path name is built by walking * the parent references all the way back to the root. * * @author Mark Norton */ public String xxgetPath () { return dir.getPath(); } public String xxgetPath2() { Vector nodes = new Vector(100); String path = null; // Collect the entry nodes between here and root. try { //nodes.add (getDisplayName()); LocalCabinet ptr = this; while (ptr.getParent() != null) { nodes.add (ptr.getDisplayName()); ptr = (LocalCabinet)ptr.getParent(); } nodes.add ("/"); // add the root. } catch (osid.filing.FilingException ex1) {} // Build the path name string. path = (String) nodes.elementAt(nodes.size()-1); for (int i = nodes.size()-2; i != -1; i--) { path += (String) nodes.elementAt(i) + "/"; } return path; } /** * Get the full file name for this entry, including path to local root. * <p> * Warning! This name cannot be used to open local files, since it does * not include rootBase. The absolute name can be created by concatenating * rootBase and getFullName(). * * @author Mark Norton */ public String getFullName() { ArrayList nodes = new ArrayList(100); String path = null; //String sep = Character.toString(this.pathSeparatorChar()); String sep = this.separator(); // Collect the entry nodes between here and root. try { // Check for the root node case. if (this.getParent() == null) { return sep; } LocalCabinet ptr = this; while (ptr.getParent() != null) { nodes.add (0, ptr.getDisplayName()); ptr = (LocalCabinet)ptr.getParent(); } //nodes.add ("/"); // add the root. } catch (osid.filing.FilingException ex1) {} // Build the path name string. path = sep + (String) nodes.get(0); for (int i = 1; i < nodes.size(); i++) { path += sep + (String) nodes.get(i); } return path; } /** * Get the File object associated with this cabinet entry. * * @author Mark Norton */ public File getFile() { return dir; } /** * Return a URL string for this LocalByteStore. */ public String getUrl() { try { return getFile().toURL().toString(); } catch (Throwable t) { Log.debug("failed to create URL from file: " + getFile(), t); return getFile().toString(); } //String fn = getFile().getAbsolutePath(); //return"file://" + fn; } /** * Get the string character that separates path nodes. */ public String separator () { LocalCabinet root = null; root = (LocalCabinet) getRootCabinet(); File file = root.getFile(); return file.separator; } /** * Get the char character that separates path nodes. */ public char separatorChar () { LocalCabinet root = (LocalCabinet) getRootCabinet(); File file = root.getFile(); return file.separatorChar; } /** * Rename the file corresponding to this byte store and update it's display * name to the new name. * * @author Mark Norton */ public void rename (String absolute, String newName) throws osid.filing.FilingException { File dst = new File (absolute); dir.renameTo (dst); updateDisplayName (newName); } }