package com.dbxml.db.core.indexer;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: IndexPattern.java,v 1.3 2004/02/12 00:17:57 bradford Exp $
 */

import java.util.StringTokenizer;

import com.dbxml.xml.NamespaceMap;
import com.dbxml.xml.SymbolTable;
import com.dbxml.xml.QName;

/**
 * IndexPattern is the internal representation of a pattern for index
 * matching purposes.
 */

public final class IndexPattern {
   public static final int PATTERN_NONE = -1;
   public static final int PATTERN_WILDCARD = -2;

   public static final int SCORE_NONE = 0;
   public static final int SCORE_WILDCARD = 1;
   public static final int SCORE_NATURAL = 2;

   private SymbolTable symbols;
   private String elemName;
   private int elemID = PATTERN_NONE;
   private String attrName;
   private int attrID = PATTERN_NONE;

   public IndexPattern(SymbolTable symbols, String pattern, NamespaceMap nsMap, boolean create) {
      this.symbols = symbols;
      StringTokenizer st = new StringTokenizer(pattern.trim(), "@");
      elemName = st.nextToken();
      if ( elemName.equals("*") )
         elemID = PATTERN_WILDCARD;
      else
         elemID = SymbolTable.getNormalizedSymbol(symbols, elemName, nsMap, create);

      if ( st.hasMoreTokens() ) {
         attrName = st.nextToken();
         if ( attrName.equals("*") )
            attrID = PATTERN_WILDCARD;
         else
            attrID = SymbolTable.getNormalizedSymbol(symbols, attrName, nsMap, create);
      }
   }

   public IndexPattern(SymbolTable symbols, String pattern, NamespaceMap nsMap) {
      this(symbols, pattern, nsMap, false);
   }

   public IndexPattern(SymbolTable symbols, int elemID) {
      this.symbols = symbols;
      this.elemID = elemID;
      elemName = convertSymbolToName(elemID);
   }

   public IndexPattern(SymbolTable symbols, int elemID, int attrID) {
      this.symbols = symbols;
      this.elemID = elemID;
      this.attrID = attrID;
      elemName = convertSymbolToName(elemID);
      attrName = convertSymbolToName(attrID);
   }

   private String convertSymbolToName(int id) {
      QName qname = symbols.getQName(id);
      String name = qname.getLocalName();
      String uri = qname.getURI();
      if ( uri != null ) {
         StringBuffer sb = new StringBuffer();
         sb.append('[');
         sb.append(uri);
         sb.append(']');
         sb.append(name);
         return sb.toString();
      }
      else
         return name;
   }

   /**
    * getMatchLevel compares this IndexPattern to p.  It then returns the
    * strength of the match betwen the two patterns from 0 to 15.
    * (0 being no match at all, 15 being perfect).  This method should be
    * called by XPath and SAX patterns to be matched against Indexer patterns.
    *
    * @param p The pattern to compare
    * @return The resulting IndexPattern strength
    */
   public int getMatchLevel(IndexPattern p) {
      int result = 0;

      switch ( p.elemID ) {
         case PATTERN_WILDCARD:
            result = (SCORE_WILDCARD << 2);
            break;

         default:
            if ( elemID == p.elemID )
               result = (SCORE_NATURAL << 2);
      }

      if ( result != 0 ) {
         switch ( p.attrID ) {
            case PATTERN_WILDCARD:
               return result + SCORE_WILDCARD;

            default:
               if ( attrID == p.attrID )
                  return result + SCORE_NATURAL;
         }
      }

      return 0;
   }

   /**
    * isInvalid returns whether or not the IndexPattern is valid.  This
    * is determined by testing whether or not the SymbolTable lookups
    * used when constructing the pattern yielding nil results.
    *
    * @return The IndexPattern's invalid status
    */
   public boolean isInvalid() {
      return elemID == PATTERN_NONE && attrID == PATTERN_NONE;
   }

   /**
    * getElementID returns the Element Symbol ID for this pattern if
    * there is one, otherwise it returns a negative value.
    *
    * @return The Element Symbol ID
    */
   public int getElementID() {
      return elemID;
   }

   /**
    * getAttributeID returns the Attribute Symbol ID for this pattern
    * if there is one, otherwise it returns a negative value.
    *
    * @return The Attribute Symbol ID
    */
   public int getAttributeID() {
      return attrID;
   }

   /**
    * getElementName returns the Element Name for this pattern if
    * there is one, otherwise it returns null.
    *
    * @return The Element Name
    */
   public String getElementName() {
      return elemName;
   }

   /**
    * getAttributeName returns the Attribute Name for this pattern if
    * there is one, otherwise it returns null.
    *
    * @return The Attribute Name
    */
   public String getAttributeName() {
      return attrName;
   }

   public int hashCode() {
      return new Integer((elemID << 16) + attrID).hashCode();
   }

   public boolean equals(Object obj) {
      if ( obj instanceof IndexPattern ) {
         IndexPattern p = (IndexPattern)obj;
         boolean eq = elemID == p.elemID && attrID == p.attrID;
         if ( eq && (elemName != null || p.elemName != null) )
            eq = elemName != null && p.elemName != null && elemName.equals(p.elemName);
         if ( eq && (attrName != null || p.attrName != null) )
            eq = attrName != null && p.attrName != null && attrName.equals(p.attrName);
         return eq;
      }
      else
         return false;
   }
}

