/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002-03 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.servlet;

import java.util.*;
import java.util.regex.*;
import org.apache.commons.lang.CharSet;
import org.apache.commons.lang.CharSetUtils;

/**
 * Represents a single entry in the browser capabilities database.
 * <P>BROWSCAP.INI managed by Gary J. Keith, &lt;http://www.garykeith.com/index.asp&gt;
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
class BrowserDatabaseEntry
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final String[] SPECIAL_CHARS = { "*.?[](){}+|^$\\" };

  private static final CharSet s_special_chars;   // set of special characters

  private static HashMap s_name_map = new HashMap();  // maps names to entries

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private String m_id;                            // ID of this entry
  private Pattern m_pattern;                      // pattern the entry matches
  private HashMap m_values = new HashMap();       // values table for this entry

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  /**
   * Constructs a new <CODE>BrowserDatabaseEntry</CODE>.
   *
   * @param header Header line for this entry.
   */
  BrowserDatabaseEntry(String header)
  {
    m_id = header.intern();
    m_pattern = Pattern.compile(preprocess(header));
    s_name_map.put(m_id,this);

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Preprocesses the header string, turning it from an MS-DOS style "glob" pattern into a proper
   * regular expression.
   *
   * @param header The header field to preprocess.
   * @return The preprocessed header.
   */
  private static final String preprocess(String header)
  {
    StringBuffer buf = new StringBuffer();
    char[] src = header.toCharArray();
    for (int i=0; i<src.length; i++)
    { // turn the DOS-style glob characters into a proper regular expression
      if (src[i]=='*')
	buf.append(".*");
      else if (src[i]=='?')
	buf.append('.');
      else
      { // append the character, possibly escaped
	if (s_special_chars.contains(src[i]))
	  buf.append('\\');
	buf.append(src[i]);

      } // end else

    } // end for

    return buf.toString();

  } // end preprocess

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Adds a new value to the values table for this element.  Used only while the database is being read and
   * initialized.
   *
   * @param name Name of the value to add to the table.
   * @param value The value to add.
   */
  void addValue(String name, String value)
  {
    name = name.toLowerCase().intern();
    m_values.put(name,value);

  } // end addValue

  /**
   * Returns the ID of this database entry.
   *
   * @return The ID of this database entry.
   */
  String getID()
  {
    return m_id;

  } // end getID

  /**
   * Tests the given string agains the pattern stored in this database entry.
   *
   * @param val The User-Agent value to test against the datanase entry.
   * @return <CODE>true</CODE> if the pattern matches against this entry, <CODE>false</CODE> if not.
   */
  boolean match(String val)
  {
    return m_pattern.matcher(val).matches();

  } // end match

  /**
   * Returns one of the values stored in this database entry.  If the value is not found in this
   * database entry, it searches the parent database entry, and so on up the chain.
   *
   * @param key The key of the value to be retrieved.
   * @return The value stored under this key.
   */
  String getValue(String key)
  {
    String val = (String)(m_values.get(key.toLowerCase()));
    if (val!=null)
      return val;
    String pname = (String)(m_values.get("parent"));
    if (pname==null)
      pname = "*";  // default entry
    BrowserDatabaseEntry p = (BrowserDatabaseEntry)(s_name_map.get(pname));
    return p.getValue(key);

  } // end getValue

  /*--------------------------------------------------------------------------------
   * Static initializer
   *--------------------------------------------------------------------------------
   */

  static
  { // initialize the character set
    s_special_chars = CharSetUtils.evaluateSet(SPECIAL_CHARS);

  } // end static initializer

} // end class BrowserDatabaseEntry
