package com.dbxml.db.core.query.helpers;

/*
 * 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: ResultSetWrapper.java,v 1.9 2004/02/12 00:17:57 bradford Exp $
 */

import com.dbxml.xml.dtsm.*;

import com.dbxml.db.core.Collection;
import com.dbxml.db.core.DBException;
import com.dbxml.db.core.data.Key;
import com.dbxml.db.core.query.NotSupportedException;
import com.dbxml.db.core.query.ProcessingException;
import com.dbxml.db.core.query.Query;
import com.dbxml.db.core.query.QueryException;
import com.dbxml.db.core.query.ResultSet;
import com.dbxml.util.UTF8;
import com.dbxml.xml.QName;
import com.dbxml.xml.SymbolTable;
import java.io.StringWriter;
import org.w3c.dom.Document;
import org.xml.sax.ContentHandler;

/**
 * ResultSetWrapper converts a ResultSet into a standard,
 * single-Document representation for wire transmission or for further
 * query processing, such as in the case of XSLT transformation.
 */

public final class ResultSetWrapper {
   public static final String RESULTS = "results";
   public static final String RESULT = "result";
   public static final String COL = "col";
   public static final String KEY = "key";
   public static final String COUNT = "count";

   private static final String PFX_RESULTS = Query.PREFIX + ":" + RESULTS;
   private static final String PFX_RESULT = Query.PREFIX + ":" + RESULT;
   private static final String PFX_NSDEF = "xmlns:" + Query.PREFIX;

   private static final String DOCUMENT_PROLOG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

   private static final String RESULTS_START =
           "<" + Query.PREFIX + ":" + RESULTS
         + " xmlns:" + Query.PREFIX + "=\"" + Query.NSURI + "\"";
   private static final String RESULTS_END = "</" + Query.PREFIX + ":" + RESULTS + ">";
   private static final String RESULT_START = "<" + Query.PREFIX + ":" + RESULT;
   private static final String RESULT_END = "</" + Query.PREFIX + ":" + RESULT + ">";

   private static final QName QNAME_RESULTS = new QName(PFX_RESULTS, Query.NSURI);
   private static final QName QNAME_RESULT = new QName(PFX_RESULT, Query.NSURI);
   private static final QName QNAME_NSDEF = new QName(PFX_NSDEF); /** @todo Fix */
   private static final QName QNAME_COL = new QName(COL);
   private static final QName QNAME_KEY = new QName(KEY);
   private static final QName QNAME_COUNT = new QName(COUNT);
   private static final byte[] UTF8_URI = UTF8.toUTF8(Query.NSURI);

   private ResultSet rs;
   private DocumentTable dt;

   public ResultSetWrapper(ResultSet rs) {
      this.rs = rs;
   }

   public void toSAX(ContentHandler handler) throws QueryException {
      throw new NotSupportedException("Not implemented");
   }

   public DocumentTable toDocumentTable() throws QueryException {
      Collection col = rs.getCollection();
      TableBuilder tb;
      try {
         tb = new TableBuilder(col.getSymbols());
      }
      catch ( DBException e ) {
         throw new NotSupportedException(e);
      }

      tb.addEntry(Constants.OBJ_BEGIN_DOCUMENT);
      int rootID = tb.addEntry(Constants.OBJ_BEGIN_ELEMENT, QNAME_RESULTS);
      // Namespace
      int attrID = tb.addEntry(Constants.OBJ_BEGIN_ATTRIBUTE, QNAME_NSDEF);
      tb.addEntry(Constants.OBJ_TEXT, UTF8_URI);
      tb.addEntry(Constants.OBJ_END_ATTRIBUTE, attrID);
      // Collection
      attrID = tb.addEntry(Constants.OBJ_BEGIN_ATTRIBUTE, QNAME_COL);
      tb.addEntry(Constants.OBJ_TEXT, UTF8.toUTF8(col.getCanonicalName()));
      tb.addEntry(Constants.OBJ_END_ATTRIBUTE, attrID);

      int count = rs.getCount();
      if ( count != -1 ) {
         attrID = tb.addEntry(Constants.OBJ_BEGIN_ATTRIBUTE, QNAME_COUNT);
         tb.addEntry(Constants.OBJ_TEXT, UTF8.toUTF8(Integer.toString(count)));
         tb.addEntry(Constants.OBJ_END_ATTRIBUTE, attrID);
      }

      while ( rs.next() ) {
         Collection c = rs.getResultCollection();

         int resID = tb.addEntry(Constants.OBJ_BEGIN_ELEMENT, QNAME_RESULT);
         if ( c != col ) {
            // Alternate Collection
            attrID = tb.addEntry(Constants.OBJ_BEGIN_ATTRIBUTE, QNAME_COL);
            tb.addEntry(Constants.OBJ_TEXT, UTF8.toUTF8(c.getCanonicalName()));
            tb.addEntry(Constants.OBJ_END_ATTRIBUTE, attrID);
         }
         Key key = rs.getResultKey();
         if ( key != null && key.getLength() > 0 ) {
            // Key
            attrID = tb.addEntry(Constants.OBJ_BEGIN_ATTRIBUTE, QNAME_KEY);
            tb.addEntry(Constants.OBJ_TEXT, key.getData());
            tb.addEntry(Constants.OBJ_END_ATTRIBUTE, attrID);
         }

         DocumentTable table = rs.getResult();
         if ( table != null ) {
            PullProcessor proc = new PullProcessor(table);
            SymbolTable symbols = table.getSymbolTable();
            while ( proc.hasNext() ) {
               DocumentTableEntry entry = proc.next();
               byte typeID = entry.getTypeID();
               int valID = entry.getValID();
               if ( Constants.isPayloadSymbol(typeID) ) {
                  if ( valID != Constants.NO_VALUE_ID ) {
                     QName qname = symbols.getQName(valID);
                     tb.addEntry(typeID, qname);
                  }
                  else
                     tb.addEntry(typeID);
               }
               else {
                  byte[] b = table.getValue(valID).getData();
                  tb.addEntry(typeID, b);
               }
            }
         }

         tb.addEntry(Constants.OBJ_END_ELEMENT, resID);
      }

      tb.addEntry(Constants.OBJ_END_ELEMENT, rootID);
      tb.addEntry(Constants.OBJ_END_DOCUMENT);
      dt = tb.buildDocumentTable();
      return dt;
   }

   public Document toDocument() throws QueryException {
      try {
         DocumentTable dt = toDocumentTable(rs);
         return DTSMHelper.tableToDocument(dt);
      }
      catch ( DTSMException e ) {
         throw new ProcessingException(e);
      }
   }

   private void writeStrings(StringWriter sw, String[] vals) {
      for ( int i = 0; i < vals.length; i++ )
         sw.write(vals[i]);
   }

   public String toText() throws QueryException {
      try {
         Collection col = rs.getCollection();

         StringWriter sw = new StringWriter(1024);
         sw.write(RESULTS_START);
         writeStrings(sw, new String[]{" ", COL, "=\"", col.getCanonicalName(), "\""});
         int count = rs.getCount();
         if ( count != -1 )
            writeStrings(sw, new String[]{" ", COUNT, "=\"", Integer.toString(count), "\""});
         sw.write(">");

         while ( rs.next() ) {
            Collection c = rs.getResultCollection();
            sw.write(RESULT_START);
            if ( c != col )
               writeStrings(sw, new String[]{" ", COL, "=\"", c.getCanonicalName(), "\""});
            Key key = rs.getResultKey();
            if ( key != null && key.getLength() > 0 )
               writeStrings(sw, new String[]{" ", KEY, "=\"", key.toString(), "\""});
            sw.write(">");

            DocumentTable table = rs.getResult();
            DTSMHelper.tableToWriter(table, sw, true);
            sw.write(RESULT_END);
         }
         sw.write(RESULTS_END);
         return sw.toString();
      }
      catch ( DTSMException e ) {
         throw new ProcessingException(e);
      }
   }

   public static void toSAX(ResultSet rs, ContentHandler handler) throws QueryException {
      new ResultSetWrapper(rs).toSAX(handler);
   }

   public static DocumentTable toDocumentTable(ResultSet rs) throws QueryException {
      return new ResultSetWrapper(rs).toDocumentTable();
   }

   public static Document toDocument(ResultSet rs) throws QueryException {
      return new ResultSetWrapper(rs).toDocument();
   }

   public static String toText(ResultSet rs) throws QueryException {
      return new ResultSetWrapper(rs).toText();
   }
}

