/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, Lotus
 * Development Corporation., http://www.lotus.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.xalan.transformer;

import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;

import org.apache.xpath.NodeSet;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.XPathContext;
import org.apache.xpath.XPathContext;
import org.apache.xpath.DOMHelper;
import org.apache.xml.utils.QName;
import org.apache.xalan.templates.KeyDeclaration;
import org.apache.xpath.XPathContext;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xpath.axes.LocPathIterator;

// import org.apache.xalan.dtm.*;

/**
 * <meta name="usage" content="advanced"/>
 * Table of element keys, keyed by document node.  An instance of this
 * class is keyed by a Document node that should be matched with the
 * root of the current context.  It contains a table of name mappings
 * to tables that contain mappings of identifier values to nodes.
 */
public class KeyTable
{

  /**
   * The document key.  This table should only be used with contexts
   * whose Document roots match this key.
   */
  private Node m_docKey;

  /**
   * Get the document root matching this key.  
   *
   *
   * @return the document root matching this key
   */
  public Node getDocKey()
  {
    return m_docKey;
  }

  /** 
   * The main iterator that will walk through the source  
   * tree for this key.
   */
  private KeyIterator m_keyIter;
  
  /**
   * Hashtable of keys.
   * The table is:
   * a) keyed by key name,
   * b) with a value that is a hashtable keyed by key values 
   * with at value of KeyRefIterator(cloned).
   */
  private Hashtable m_defsTable;

  /**
   * Build a keys table.
   * @param doc The owner document key.
   * @param nscontext The stylesheet's namespace context.
   * @param name The key name
   * @param keyDeclarations The stylesheet's xsl:key declarations.
   * @param xmlLiaison The parser liaison for support of getNodeData(useNode).
   *
   * @throws javax.xml.transform.TransformerException
   */
  public KeyTable(
          Node doc, PrefixResolver nscontext, QName name, Vector keyDeclarations, XPathContext xmlLiaison)
            throws javax.xml.transform.TransformerException
  {

    m_docKey = doc;
    m_keyIter = new KeyIterator(doc, nscontext, name, keyDeclarations,
                                xmlLiaison);
    m_keyIter.setKeyTable(this);
  }  

  /**
   * Given a valid element key, return the corresponding node list.
   * 
   * @param The name of the key, which must match the 'name' attribute on xsl:key.
   * @param ref The value that must match the value found by the 'match' attribute on xsl:key.
   * @return If the name was not declared with xsl:key, this will return null,
   * if the identifier is not found, it will return null,
   * otherwise it will return a LocPathIterator instance.
   */
  public LocPathIterator getNodeSetByKey(QName name, String ref)
  {

    KeyIterator ki;
    KeyRefIterator kiRef;
    Hashtable refsTable = null;

    // First look for the key in the existing key names table
    if (m_defsTable != null)
    {
      refsTable = (Hashtable)m_defsTable.get(name);
      if (refsTable != null)
      {
        Object kiObj = refsTable.get(ref);
        if (kiObj != null)
        {
          // An entry already exists for this key name and value.
          // Return a clone of the node iterator found.
          try
          {
            // clone with reset??
            kiRef = (KeyRefIterator)((KeyRefIterator)kiObj).clone();
            return kiRef;
          }
          catch (CloneNotSupportedException cnse)
          {
            ki = null;
          }
        }
      }
    }

    // No entry was found for this key name and value. Create one.
    {
      if (m_defsTable == null)
        m_defsTable = new Hashtable();
      if (refsTable == null)
        refsTable = new Hashtable();
      
      // initialize walker only once!
      if (m_keyIter.getFirstWalker().getRoot() == null)
        m_keyIter.setLookupKey(ref);
      else
        ((KeyWalker)m_keyIter.getFirstWalker()).m_lookupKey = ref;
      kiRef = new KeyRefIterator(ref, m_keyIter);
      refsTable.put(ref, kiRef);
      m_defsTable.put(name,refsTable);
      return kiRef;              
    } 
  }

  /**
   * Get Key Name for this KeyTable  
   *
   *
   * @return Key name
   */
  public QName getKeyTableName()
  {
    return m_keyIter.getName();
  }
  
  /**
   * Add this node to the nodelist matching this key value. 
   * If there was no existing entry for that key value, create
   * one.   
   *
   * @param ref Key ref(from key use field)
   * @param node Node matching that ref 
   */
  void addRefNode(String ref, Node node)
  {
    KeyRefIterator kiRef = null;
    Hashtable refsTable = null;
    if (m_defsTable != null)
    {
      refsTable = (Hashtable)m_defsTable.get(getKeyTableName());
      if (refsTable != null)
      {
        Object kiObj = refsTable.get(ref);
        if (kiObj != null)
        {          
          kiRef = (KeyRefIterator)kiObj;            
        }
      }
    }
    if (kiRef == null)
    {  
      if (m_defsTable == null)
        m_defsTable = new Hashtable();
      if (refsTable == null)
      {  
        refsTable = new Hashtable();
        m_defsTable.put(getKeyTableName(),refsTable);
      }
      kiRef = new KeyRefIterator(ref, m_keyIter);
      refsTable.put(ref, kiRef);      
    }
    kiRef.addNode(node); 
  }
  
  
}
