package com.dbxml.xml;

/*
 * 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: SymbolTable.java,v 1.2 2004/02/12 00:17:58 bradford Exp $
 */

import com.dbxml.xml.XMLSerializable;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * SymbolTable is a class that allows the dbXML Compression system to
 * resolve symbol IDs in compression/decompression passes.
 */

public class SymbolTable implements XMLSerializable {
   static final String SYMBOLS = "symbols";
   static final String SYMBOL = "symbol";
   static final String NAME = "name";
   static final String NSURI = "nsuri";
   static final String ID = "id";

   private transient boolean dirty;
   private transient long lastModified = System.currentTimeMillis();

   private Map symbols = Collections.synchronizedMap(new HashMap()); // String to SymbolInfo
   private Map names = Collections.synchronizedMap(new HashMap());   // Integer to SymbolInfo
   private int maxSymbol = -1;

   public SymbolTable() {
   }

   public SymbolTable(Element elem) {
      streamFromXML(elem);
   }

   public static final String getLookupName(QName qname) {
      String uri = qname.getURI();
      if ( uri != null ) {
         StringBuffer sb = new StringBuffer(128);
         sb.append('[');
         sb.append(qname.getURI());
         sb.append(']');
         sb.append(qname.getLocalName());
         return sb.toString();
      }
      else
         return qname.getName();
   }

   public static final int getNormalizedSymbol(SymbolTable symbols, String lookup, NamespaceMap nsMap, boolean create) {
      if ( lookup.startsWith("[") ) {
         int idx = lookup.indexOf(']');
         String uri = lookup.substring(1, idx);
         String name = lookup.substring(idx + 1);
         QName qname = getNormalizedQName(new QName(name, uri));
         return symbols.getSymbol(qname, create);
      }
      else {
         QName qname = new QName(lookup, null);
         String prefix = qname.getPrefix();
         if ( prefix != null ) {
            String uri = (String)nsMap.get(prefix);
            qname = getNormalizedQName(new QName(lookup, uri));
         }
         return symbols.getSymbol(qname, create);
      }
   }

   public static final QName getNormalizedQName(QName qname) {
      String uri = qname.getURI();
      if ( uri != null ) {
         String localName = qname.getLocalName();
         StringBuffer sb = new StringBuffer(32);
         sb.append("ns");
         sb.append(Integer.toString(uri.hashCode()));
         sb.append(':');
         sb.append(localName);
         return new QName(sb.toString(), uri);
      }
      else
         return qname;
   }

   public final boolean isDirty() {
      return dirty;
   }

   public final void setDirty(boolean dirty) {
      this.dirty = dirty;
      lastModified = System.currentTimeMillis();
   }

   public final SymbolInfo getSymbolInfo(QName qname) {
      String lookupName = getLookupName(qname);
      return (SymbolInfo)symbols.get(lookupName);
   }

   public final int getSymbol(QName qname, boolean create) {
      String lookupName = getLookupName(qname);
      SymbolInfo info = (SymbolInfo)symbols.get(lookupName);
      if ( info != null )
         return info.symbol;
      else if ( create ) {
         synchronized ( symbols ) {
            int id = ++maxSymbol;
            info = new SymbolInfo(qname, id);
            symbols.put(lookupName, info);
            names.put(new Integer(id), info);
            setDirty(true);
            return id;
         }
      }
      return -1;
   }

   public final int getSymbol(QName qname) {
      return getSymbol(qname, false);
   }

   public final SymbolInfo getSymbolInfo(int symbol) {
      return (SymbolInfo)names.get(new Integer(symbol));
   }

   public final QName getQName(int symbol) {
      SymbolInfo info = getSymbolInfo(symbol);
		if ( info != null )
			return info.qname;
		else
			return null;
   }

   public final long getLastModified() {
      return lastModified;
   }

   public final Element streamToXML(Document doc) throws DOMException {
      Element root = doc.createElement(SYMBOLS);

      Iterator list = symbols.values().iterator();
      while ( list.hasNext() ) {
         SymbolInfo info = (SymbolInfo)list.next();
         QName qname = info.qname;
         String uri = qname.getURI();
         Element e = doc.createElement(SYMBOL);
         e.setAttribute(NAME, qname.getName());
         if ( uri != null && uri.length() > 0 )
            e.setAttribute(NSURI, uri);
         e.setAttribute(ID, Integer.toString(info.symbol));
         root.appendChild(e);
      }
      return root;
   }

   public final void streamFromXML(Element element) throws DOMException {
      symbols.clear();
      names.clear();
      maxSymbol = -1;

      NodeList list = element.getElementsByTagName(SYMBOL);
      int size = list.getLength();
      for ( int i = 0; i < size; i++ ) {
         Element elem = (Element)list.item(i);

         String name = elem.getAttribute(NAME);
         String uri = elem.getAttribute(NSURI);
         if ( uri != null && uri.length() == 0 )
            uri = null;
         QName qname = new QName(name, uri);

         Integer id = new Integer(elem.getAttribute(ID));
         if ( id.shortValue() > maxSymbol )
            maxSymbol = id.shortValue();

         SymbolInfo info = new SymbolInfo(qname, id.shortValue());
         String lookupName = getLookupName(qname);
         symbols.put(lookupName, info);
         names.put(id, info);
      }
   }

   public final class SymbolInfo implements Serializable {
      public QName qname;
      public int symbol;

      public SymbolInfo(QName qname, int symbol) {
         this.qname = qname;
         this.symbol = symbol;
      }

      public QName getQName() {
         return qname;
      }

      public int getSymbolID() {
         return symbol;
      }
   }
}



