package basics.xml;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import basics.testing.Test;

/**
 * This class represents a TPath
 * @author ks TPath is designed to navigate and compare paths, toString concatenates all paths and
 * separates each with a DELIMITER
 */
public class TPath implements Serializable
{
private static final long serialVersionUID = 1L;
private Vector<String>    path             = null;
private String            DELIMITER        = "$";
private String            prefix           = "";
private String            suffix           = "";

public TPath()
{
   path = new Vector<String>();
}

public TPath(TPath tp)
{
   DELIMITER = tp.getDelimiter();
   path = tp.getPath();
}

public TPath(String delimiter)
{
   this();
   DELIMITER = delimiter;
}

/**
 * parse a string devided by delimiter and append the results to the TPath vector
 */
public TPath parseAndAppend(String buffer, String delimiter)
{
   StringTokenizer st = new StringTokenizer(buffer, delimiter);
   while(st.hasMoreElements())
      append((String)st.nextElement());
   return this;
}

/** return the internal vector */
public Vector<String> getPath()
{
   return path;
}

/** return depth of the path */
public int getDepth()
{
   return path.size();
}

/** append an element (String) to the internal vector */
public void append(String p)
{
   path.add(p);
}

/** append an element (String) to the internal vector */
public void appendAndParse(String p)
{
   if(p == null) return;
   String [] elements = p.split(DELIMITER);
   for(int i = 0; i < elements.length; i++)
   {
      String e = elements[i];
      if(e.length() > 0) path.add(e);
   }
}

/** remove the last element */
public void removeLast()
{
   if(getDepth() < 1) return;
   path.remove(path.lastElement());
}

/** return enumeration for iterating through the path */
@SuppressWarnings("unchecked")
public Enumeration elements()
{
   return path.elements();
}

/**
 * return an Enumeration, which accumulates the next and all previous elements into a TPath
 */
@SuppressWarnings("unchecked")
public Enumeration accumulator()
{
   return new Accumulator(this);
}

/** get the last element: i.e. the filename in a file directory tree */
public String getLast()
{
   if(getDepth() < 1) return "";
   return (String)path.lastElement();
}

/** check if a string is equal to an string representation of the path */
public boolean equals(String t)
{
   String s = toString();
   return(s.equals(t));
}

/**
 * check if a string is equal to an string representation of the path, ignore case
 */
public boolean equalsIgnoreCase(String t)
{
   String s = toString();
   return(s.equalsIgnoreCase(t));
}

/** check if two TPath elements are equal */
public boolean equals(TPath t)
{
   return(path.equals(t.getPath()));
}

/** check if this is the direct parent of another TPath */
public boolean isParent(TPath tp)
{
   if(tp.getDepth() == 0) return false;
   TPath t = new TPath(tp);
   t.removeLast();
   return t.equals(this);
}

/** check if this is the direct child of another TPath */
public boolean isChild(TPath tp)
{
   if(getDepth() == 0) return false;
   TPath t = new TPath(this);
   t.removeLast();
   return t.equals(tp);
}

/** check if this is a super element of TPath t */
@SuppressWarnings("unchecked")
public boolean isSuper(TPath t)
{
   if(getDepth() >= t.getDepth()) return false;
   int p = 0;
   Vector<String> v = t.getPath();
   for(Enumeration e = elements(); e.hasMoreElements();)
   {
      String s = (String)e.nextElement();
      try
      {
         if(!s.equals((String)v.elementAt(p))) return false;
      }
      catch(ArrayIndexOutOfBoundsException ex)
      {
         return false;
      }
      p++;
   }
   return true;
}

/** check if this is a subordinate element of TPath t */
@SuppressWarnings("unchecked")
public boolean isSub(TPath t)
{
   if(getDepth() <= t.getDepth()) return false;
   int p = 0;
   for(Enumeration e = t.elements(); e.hasMoreElements();)
   {
      String s = (String)e.nextElement();
      try
      {
         if(!s.equals((String)path.elementAt(p))) return false;
      }
      catch(ArrayIndexOutOfBoundsException ex)
      {
         return false;
      }
      p++;
   }
   return true;
}

/** check if this is the root (empty) */
public boolean isRoot()
{
   return getDepth() == 0;
}

/** return string representation, insert delimiters */
@SuppressWarnings("unchecked")
public String toString()
{
   if(getDepth() < 1) return prefix;
   StringBuilder sb = new StringBuilder("");
   for(Enumeration e = path.elements(); e.hasMoreElements();)
   {
      sb.append(e.nextElement());
      if(e.hasMoreElements()) sb.append(DELIMITER);
   }
   String s = sb.toString();
   if(s.endsWith(DELIMITER)) s = s.substring(0, s.length() - 1);
   return prefix + s + suffix;
}

public String getDelimiter()
{
   return DELIMITER;
}

@SuppressWarnings("unchecked")
class Accumulator implements Enumeration
{
private TPath       extent = null;
private Enumeration source = null;

public Accumulator(TPath tp)
{
   source = tp.elements();
   extent = new TPath(tp.getDelimiter());
}

/** if if there are more elements */
public boolean hasMoreElements()
{
   return source.hasMoreElements();
}

/** get the next element, using the index */
public Object nextElement()
{
   extent.append((String)source.nextElement());
   return new TPath(extent);
}
}

public void setPrefix(String prefix)
{
   this.prefix = prefix;
}

public void setSuffix(String suffix)
{
   this.suffix = suffix;
}

@SuppressWarnings("unchecked")
public Iterator<String> iterator()
{
   final Enumeration<String> e = elements();
   return new Iterator<String>()
   {
      public boolean hasNext()
      {
         return e.hasMoreElements();
      }

      public String next()
      {
         return e.nextElement();
      }

      public void remove()
      {
      }
   };
}

public static void unittest()
{
   TPath p = new TPath();
   p.append("a");
   p.append("b");
   p.append("c");
   Test.assertTrue(p.getDepth() == 3, "depth not 3");
   Test.assertEqualsTrue(p.toString(), "a$b$c", "wrong path");
   Test.assertEqualsTrue(p.getLast(), "c", "last element not c");
   p.removeLast();
   Test.assertEqualsTrue(p.getLast(), "b", "last element not b");
   p.removeLast();
   Test.assertEqualsTrue(p.getLast(), "a", "last element not a");
   p.setPrefix("<");
   p.setSuffix(">");
   Test.assertEqualsTrue(p.toString(), "<a><b><c>", "wrong path");
}
}
