/*
* This code 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 code 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 program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
package no.geosoft.cc.directory;
import java.util.*;
/**
* A class representing a generic folder node within a directory
* structure. A folder may contain DirectoryEnty children and may
* have a client specified item back-end object.
*
* @author <a href="mailto:jacob.dreyer@geosoft.no">Jacob Dreyer</a>
*/
public class Folder extends DirectoryEntry
{
private List entries_; // of DirectoryEntry
private boolean isExpanded_;
/**
* Create a new folder entry.
*
* @param item Client specific item of the folder.
* @param name Name of the folder.
*/
public Folder (Object item, String name)
{
super (item, name);
entries_ = new ArrayList();
isExpanded_ = false;
}
/**
* Create a folder with name according to item toString().
*
* @param item Client specific item of the folder.
*/
public Folder (Object item)
{
this (item, item.toString());
}
/**
* Create a namelsee and itemless folder entry.
*
* @return
*/
public Folder()
{
this (null, null);
}
/**
* Add an entry to this folder at a specific position.
* NOTE: Entries can have one parent only.
*
* @param entry Entry to add.
* @param index Entry position, 0 is first, etc.
*/
public void add (DirectoryEntry entry, int index)
{
entry.setParent (this);
entries_.add (index, entry);
}
/**
* Add an entry to the end of this folder.
*
* @param entry Entry to add.
*/
public void add (DirectoryEntry entry)
{
add (entry, getNEntries());
}
/**
* Add a set of entries to this folder.
*
* @param entries Entries to add.
*/
public void add (Collection entries)
{
for (Iterator i = entries.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
add (entry);
}
}
/**
* Add an entry sorted alphabetically according to the entrys
* toString() method.
*
* @param entry Entry to add.
*/
public void addSorted (DirectoryEntry entry)
{
int index = 0;
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry existingEntry = (DirectoryEntry) i.next();
if (existingEntry.compareTo (entry) > 0)
break;
index++;
}
add (entry, index);
}
/**
* Add a set of entries sorted alphabetically according to the entries'
* toString() method.
*
* @param entry Entry to add.
*/
public void addSorted (Collection entries)
{
for (Iterator i = entries.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
addSorted (entry);
}
}
/**
* Remove entry from this folder.
*
* @param entry Entry to remove.
*/
public void remove (DirectoryEntry entry)
{
// Everybody that "is" this instance must be removed as well
entry.removeSymbolicLinks();
// Remove from child list
entries_.remove (entry);
}
/**
* Return true if this is a leaf node. A folder is per definition not
* a leaf node even if empty, so this method always return false.
*
* @return False always.
*/
public boolean isLeaf()
{
return false; // Per definition, even if it is empty
}
/**
* Return true if this is a root node. A node is root if it doesn't
* have a parent node.
*
* @return True if this is a root node, false otherwise.
*/
public boolean isRoot()
{
return getParent() == null;
}
/**
* Return the child entry at the specific position. Return null
* if the entry does not exists.
*
* @param index Position of entry to return.
* @return
*/
public DirectoryEntry getEntry (int index)
{
if (index < 0 || index > entries_.size()-1)
return null;
else
return (DirectoryEntry) entries_.get (index);
}
/**
* Staring from this node, return the index'th element in the
* tree, traversing depth first.
*
* @param entryNo Entry number to find.
* @return
*/
public DirectoryEntry getGlobalEntry (int entryNo)
{
Vector list = new Vector();
return getGlobalEntry (entryNo, list);
}
private DirectoryEntry getGlobalEntry (int entryNo, Vector list)
{
if (list.size() == entryNo) return this;
list.add (this);
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry subEntry = (DirectoryEntry) i.next();
if (subEntry.isFolder()) {
Folder folder = (Folder) subEntry;
DirectoryEntry foundEntry = folder.getGlobalEntry (entryNo, list);
if (foundEntry != null) return foundEntry;
}
else {
if (list.size() == entryNo) return subEntry;
list.add (subEntry);
}
}
return null;
}
/**
* Starting from here, return the entry number of the specified
* entry traversing depth first. Return -1 if not found.
*
* @param entry Entry to find entry number of.
* @return Entry number of specified entry (or -1 if not found).
*/
public int getGlobalIndexOfEntry (DirectoryEntry entry)
{
Vector list = new Vector();
boolean isFound = getGlobalIndexOfEntry (entry, list);
return isFound ? list.size() : -1;
}
private boolean getGlobalIndexOfEntry (DirectoryEntry entry, Vector list)
{
if (this == entry) return true;
list.add (this);
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry subEntry = (DirectoryEntry) i.next();
if (subEntry.isFolder()) {
Folder folder = (Folder) subEntry;
boolean isFound = folder.getGlobalIndexOfEntry (entry, list);
if (isFound) return true;
}
else {
list.add (subEntry);
if (subEntry == entry) return true;
}
}
return false;
}
/**
* Return position of the specified entry. Null if entry does not
* exist.
*
* @param entry Entry to find index of.
* @return
*/
public int getIndexOfEntry (DirectoryEntry entry)
{
return entries_.indexOf (entry);
}
/**
* Check if the folder is empty (has no children).
*
* @return True if the folder is empty, false otherwise.
*/
public boolean isEmpty()
{
return getNEntries() == 0;
}
/**
* Return number of entries in this folder.
*
* @return Number of entries in this folder.
*/
public int getNEntries()
{
return entries_.size();
}
/**
* Get the entries of this folder.
*
* @return The entries of this folder (List of DirectoryEntry)
*/
public List getEntries()
{
return entries_;
}
/**
* Recursively check if an entry is descendant of this folder.
*
* @param entry Entry to check.
* @return True if the entry is a descendant of this folder,
* false otherwise.
*/
public boolean contains (DirectoryEntry entry)
{
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry subEntry = (DirectoryEntry) i.next();
if (subEntry == entry) return true;
if (subEntry instanceof Folder &&
((Folder)subEntry).contains (entry))
return true;
}
return false;
}
/**
* Find a given node among the immediate children of this
* folder.
*
* @param name Name of child to find.
* @return Directory entry of searched node (or null if not found).
*/
public DirectoryEntry find (String name)
{
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry.getName().equals (name))
return entry;
}
return null;
}
/**
* Find a given node within the entire subtree of this folder.
*
* @param name Name of node to find.
* @return Directory entry of searched node (or null if not found).
*/
public DirectoryEntry findGlobal (String name)
{
if (getName().equals (name)) return this;
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry instanceof Folder) {
DirectoryEntry foundEntry = ((Folder) entry).findGlobal (name);
if (foundEntry != null) return foundEntry;
}
else {
if (entry.getName().equals (name))
return entry;
}
}
// Not found
return null;
}
/**
* Find a given folder among the immediate children of this
* folder.
*
* @param name Name of folder to find.
* @return The searched folder (or null if not found).
*/
public Folder findFolder (String name)
{
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry.isFolder() && entry.getName().equals (name))
return (Folder) entry;
}
return null;
}
private boolean isItemsEqual (Object o1, Object o2)
{
if (o1 == null)
return o2 == null;
else
return o1.equals (o2);
}
/**
* Return sub folder based on item value. Search the entire
* sub tree from this folder.
*
* @param item Item of the folder to find.
* @return Searched folder (or null if not found).
*/
public Folder findFolder (Object item)
{
if (getItem() == item) return this;
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry.isFolder()) {
Folder folder = (Folder) entry;
Folder found = folder.findFolder (item);
if (found != null) return found;
}
}
// Not found
return null;
}
/**
* Return entry based on item value. Search the entire
* sub tree from this folder.
*
* @param item Item of the entry to find.
* @return Searched entry (or null if not found).
*/
public DirectoryEntry findEntry (Object item)
{
if (isItemsEqual (item, getItem())) return this;
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry.isFolder()) {
Folder folder = (Folder) entry;
DirectoryEntry found = folder.findEntry (item);
if (found != null) return found;
}
else {
if (isItemsEqual (item, entry.getItem()))
return entry;
}
}
// Not found
return null;
}
/**
* Identify this folder as "expanded". This setting may be used
* in a GUI front-end module.
*/
public void expand()
{
isExpanded_ = true;
}
/**
* Identify this folder and all sub folders as "expanded".
* This setting may be used in a GUI front-end module.
*/
public void expandAll()
{
// Expand this
expand();
// Expand children folders
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry.isFolder()) {
Folder folder = (Folder) entry;
folder.expandAll();
}
}
}
/**
* Identify this folder as "collapsed". This setting may be used
* in a GUI front-end module.
*/
public void collapse()
{
isExpanded_ = false;
}
/**
* Identify this folder and all sub folders as "collapsed".
* This setting may be used in a GUI front-end module.
*/
public void collapseAll()
{
// Expand this
collapse();
// Expand children folders
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
if (entry.isFolder()) {
Folder folder = (Folder) entry;
folder.collapseAll();
}
}
}
// DEBUG
public void print (int indent)
{
for (int i = 0; i < indent; i++)
System.out.print (" ");
System.out.println ("Folder: " + getName());
for (Iterator i = entries_.iterator(); i.hasNext(); ) {
DirectoryEntry entry = (DirectoryEntry) i.next();
entry.print (indent + 2);
}
}
}