/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.cache;


import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Fully qualified name. Essentially a list of relative names, from root
 * to a given node. This class is immutable.
 *
 * @version $Revision: 1.6.2.8 $
 */
public class Fqn implements Cloneable, Externalizable {
   // static final transient Logger log=Logger.getLogger(Fqn.class.getName());
   private List elements=new ArrayList(5);

   public Fqn() {
   }

   public Fqn(Object name) {
      add(name);
   }

   public Fqn(List names) {
      for(Iterator it=names.iterator(); it.hasNext();) {
         Object name=it.next();
         add(name);
      }
   }

   public Fqn(Object[] names) {
      if(names != null)
         for(int i=0; i < names.length; i++)
            add(names[i]);
   }

   public Fqn(Fqn base, Object relative_name) {
      elements.addAll(base.elements);
      elements.add(relative_name);
   }

   public Fqn(Fqn base, Fqn relative) {
      elements.addAll(base.elements);
      elements.addAll(relative.elements);
   }

   public static Fqn fromString(String fqn) {
      Fqn retval=new Fqn();
      retval.addFqn(fqn);
      return retval;
   }

   private void add(Object relative_name) {
      elements.add(relative_name);
   }

   /**
    * Obtain a child Fqn from a sub-index.
    *
    * @param index where is the last index of child fqn
    * @return A Fqn child object.
    */
   public Fqn getFqnChild(int index) {
      List list=elements.subList(0, index);
      return new Fqn(list.toArray());
   }

   /**
    * Constructs an Fqn out of a string such as "/a/b/c"
    *
    * @param fqn
    */
   public void addFqn(String fqn) {
      String child_name;
      StringTokenizer tok;

      if(fqn == null) return;
      tok=new StringTokenizer(fqn, TreeCache.SEPARATOR);
      while(tok.hasMoreTokens()) {
         child_name=tok.nextToken();
         add(child_name);
      }
   }

   public Fqn add(List names) {
      if(names != null)
         elements.addAll(names);
      return this;
   }

   public int size() {
      return elements.size();
   }

   public Object get(int index) {
      return elements.get(index);
   }


   public Object clone() {
      return new Fqn(elements);
   }

   public boolean equals(Object obj) {
      Fqn other;
      Object o1, o2;
      int len;

      if(obj == null)
         return false;
      other=(Fqn)obj; // might throw a ClassCastException
      if(other.size() != (len=size()))
         return false;
      for(int i=0; i < len; i++) {
         o1=elements.get(i);
         o2=other.get(i);
         if(o1 == null && o2 != null) {
            return false;
         }
         if(o1 != null && !o1.equals(o2)) {
            return false;
         }
      }
      return true;
   }

   public int hashCode() {
      int hash_code=0;
      Object o;

      for(Iterator it=elements.iterator(); it.hasNext();) {
         o=it.next();
         hash_code+=o == null ? 0 : o.hashCode();
      }
      return hash_code;
   }

   public String toString() {
      StringBuffer sb=new StringBuffer();
      for(Iterator it=elements.iterator(); it.hasNext();) {
         sb.append(TreeCache.SEPARATOR).append(it.next());
      }
      return sb.length() == 0 ? TreeCache.SEPARATOR : sb.toString();
   }


   public void writeExternal(ObjectOutput out) throws IOException {
      out.writeObject(elements);
   }

   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
      elements=(List)in.readObject();
   }


   /**
    * Check if current fqn is child of parentFqn
    *
    * @param parentFqn
    * @return
    */
   public boolean isChildOf(Fqn parentFqn) {
      List parentList=parentFqn.elements;
      // If they have the same size or the parent node list is longer,
      // then parentFqn can't be the parent of this Fqn
      if( parentList.size() >= elements.size() ) return false;
      for(int i=0; i < parentList.size(); i++) {
         if(!parentList.get(i).equals(elements.get(i))) return false;
      }
      return true;
   }

}
