package com.cosylab.cdb.jdal;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration)
* and Cosylab 2002, All rights reserved
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on Jun 29, 2003
* This class represents a node in a small tree model.
* The node has name and eventually child list.
* The <code>ArrayList</code> is used for child storage
* and not a <code>HashMap</code> since it is expected that
* number of childs is quite small.
* The <code>loadNodes</code> curently scans file system at
* given path to create the tree but it can be overriden for
* differnt needs.
* This will be mainly used for the <code>DAL::list_nodes</code>
* but it is generic enough for other purposes.
* jDAL implementation will construct a root node by static getRoot()
* function which will scan given file path and fill hierarchy of
* nodes (curls) that jDAL can return.
*
* @author dvitas
*
*/
public class DALNode {
protected String name; // the name of this node.
protected ArrayList childs; // the childs if any
protected DALNode parent; // parent node. The root node has a null for this.
/**
* Constructs a root node which holds the hierarchy of all
* valid curls given by path <code>filePath</code>
*
* @param filePath the path where scan begins this is ususaly the DAL root path
* @return an instance of DALNode that has all hierarchy inside itself.
*/
public static DALNode getRoot(String filePath) {
// create a root node
DALNode rootNode = new DALNode(filePath, null);
// and recursively fills all its childs
rootNode.loadNodes(filePath);
return rootNode;
}
/**
* Constructs this object.
*
* @param name The name of this node
* @param parent parent node for this node
*/
public DALNode(String name, DALNode parent) {
super();
this.name = name;
this.parent = parent;
}
/**
* Returns a string that holds child names of the given path
* @param path The path where to start i.e. MACI/Managers
*
* @return a string of the child names delimited with a space
*/
public String list(String path) {
// create at least an empty string as return val
StringBuffer sbuf = new StringBuffer(50);
DALNode node = findNode(path);
// if node exists append its childs separated by a space
for( int i=0; node!=null && node.childs!=null && i<node.childs.size(); i++ ) {
DALNode currNode = (DALNode)node.childs.get(i);
sbuf.append(currNode.name);
sbuf.append(' ');
}
return new String(sbuf);
}
/**
* Returns the node for the given path or <code>null</code> if the
* path does not exist under this tree.
*
* @param path The path to find i.e. MACI/Managers
* @return The child node for path or <code>null</code> if it doesn't exist
*/
protected DALNode findNode(String path) {
// use a StringTokenizer to simplifies the path parsing
StringTokenizer st = new StringTokenizer(path, "/");
if( !st.hasMoreTokens() )
return this;
// we should have a child with the name of first part of path
// i.e. if we a root node and we get the path as /MACI/Managers/Manager
// then we should have a child with name 'MACI' in this node
String nodeName = st.nextToken();
DALNode child = getNode(nodeName);
// if we don't then we are unable to find this path
if( child == null )
return null;
// if we have more tokens i.e. 'Managers' then delegate to the child node
// next part of the path and it will do the same for its childs and so for
if( st.hasMoreTokens() )
return child.findNode(path.substring(path.indexOf(nodeName)+nodeName.length()));
// if no more parts in path then we have the node
return child;
}
/**
* Scans the path given by <code>filePath</code> and creates nodes
* as childs for this node for all XML files that has name as its
* parent directory.
*
* @param filePath The path where to start scan i.e. $ACS_CDB
*/
protected void loadNodes(String filePath) {
String fileName;
File base = new File(filePath);
File[] basefiles = base.listFiles();
// scan a tree and add only xml files with the same name as its parent dir
for (int i = 0; basefiles != null && i < basefiles.length; i++) {
fileName = basefiles[i].getName();
if(!fileName.endsWith(".xml")) {
loadNodes(basefiles[i].getPath());
continue;
}
// xmls are valid jDAL nodes if they have name as its parent dir
String parentDirName = basefiles[i].getParentFile().getName();
if( parentDirName.equals(fileName.substring(0,fileName.length()-4)))
addNode(basefiles[i].getPath().substring(name.length()));
}
}
/**
* Adds a child node to this node at the proper place given by <code>nodePath<code>
* The nodePath can be delimited by <code>File.separatorChar</code>.
*
* @param nodePath The path of the node i.e. MACI/Managers/Manager/Manager.xml
*/
protected void addNode(String nodePath) {
// if this node doesn't have childs jet, create them
if(childs == null )
childs = new ArrayList(1);
// again use tokenizer to get rid of extra '/' in path
// everything is as in findNode function.
// If we get /MACI/Managers/Manager/Manager.xml we first create a node
// 'MACI' in this node if it doesn't exists and delegete to it the rest
// of the path.
StringTokenizer st = new StringTokenizer(nodePath, String.valueOf(File.separatorChar));
if( !st.hasMoreTokens() )
return;
String nodeName = st.nextToken();
DALNode child = getNode(nodeName);
if( child == null ) {
child = new DALNode(nodeName, this);
childs.add(child);
}
if( st.hasMoreTokens() )
child.addNode(nodePath.substring(nodePath.indexOf(nodeName)+nodeName.length()));
}
/**
* Returns a sibling child node with given name
*
* @param nodeName The name of child node
* @return The child node or <code>null</code> it it doesn't exists
*/
protected DALNode getNode(String nodeName) {
// simply iterate through childs array since it is not expected
// that we will have a lot of childs in node since we use hierarchy
// to logicaly divide parts and therefore this shouldn't be a performance issue
for( int i=0; childs != null && i<childs.size(); i++ ) {
DALNode currNode = (DALNode)childs.get(i);
if( currNode.name.equals(nodeName))
return currNode;
}
return null;
}
/**
* Fill given list with nodes
*
* @param nodeName The name of child node
* @return The child node or <code>null</code> it it doesn't exists
*/
protected void getNodes(List list) {
if(childs == null) {
list.add(getCurl());
}
for( int i=0; childs != null && i<childs.size(); i++ ) {
DALNode currNode = (DALNode)childs.get(i);
currNode.getNodes(list);
}
}
public String getCurl() {
DALNode node = getCurlNode();
if(node == null)
return null;
// go up to the root and compose path
String curl = "";
DALNode curr = node.parent;
while(curr != null && curr.parent != null) {
curl = "/" + curr.name + curl;
curr = curr.parent;
}
return curl;
}
/**
* Prints the hierarchy on System.out indenting child nodes
* This function is used for debuging purposes.
*
* @param level The indentation level which will be recursevly increased
*/
public void print(int level) {
// adjust the indent
for( int j=0; j<level; j++)
System.out.print( " " );
// print our name
System.out.println(name);
// and let the childs do the same
for( int i=0; childs != null && i<childs.size(); i++ ) {
DALNode currNode = (DALNode)childs.get(i);
currNode.print(level+1);
}
}
/**
* @return
*/
public DALNode getCurlNode() {
// curl node is first node without childs
for( int i=0; childs != null && i<childs.size(); i++ ) {
DALNode currNode = (DALNode)childs.get(i);
if(currNode.childs == null)
return currNode;
}
return null;
}
/**
* @return
*/
public DALNode[] getChilds() {
ArrayList list = new ArrayList();
for( int i=0; childs != null && i<childs.size(); i++ ) {
DALNode currNode = (DALNode)childs.get(i);
if(currNode.childs == null)
continue;
list.add(currNode);
}
DALNode[] childs = new DALNode[list.size()];
list.toArray(childs);
return childs;
}
/**
* Returns true if this node is without any hierarchy - just plain node
* @return
*/
public boolean isSimple() {
if(childs==null || childs.size() > 1)
return false;
DALNode firstChild = (DALNode)childs.get(0);
if(firstChild.childs != null)
return false;
return true;
}
public boolean hasXmlChild() {
boolean hasXmlChild = ( findNode(this.name + ".xml") != null );
return hasXmlChild;
}
}