/*
 * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package com.sun.rowset.internal;

import java.util.*;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

import java.sql.*;
import javax.sql.*;

import javax.sql.rowset.*;
import com.sun.rowset.*;
import java.io.IOException;
import java.text.MessageFormat;

/**
 * The document handler that receives parse events that an XML parser sends while it
 * is parsing an XML document representing a <code>WebRowSet</code> object. The
 * parser sends strings to this <code>XmlReaderContentHandler</code> and then uses
 * these strings as arguments for the <code>XmlReaderContentHandler</code> methods
 * it invokes. The final goal of the SAX parser working with an
 * <code>XmlReaderContentHandler</code> object is to read an XML document that represents
 * a <code>RowSet</code> object.
 * <P>
 * A rowset consists of its properties, metadata, and data values. An XML document
 * representating a rowset includes the values in these three categories along with
 * appropriate XML tags to identify them.  It also includes a top-level XML tag for
 * the rowset and three section tags identifying the three categories of values.
 * <P>
 * The tags in an XML document are hierarchical.
 * This means that the top-level tag, <code>RowSet</code>, is
 * followed by the three sections with appropriate tags, which are in turn each
 * followed by their constituent elements. For example, the <code>properties</code>
 * element will be followed by an element for each of the properties listed in
 * in this <code>XmlReaderContentHandler</code> object's <code>properties</code>
 * field.  The content of the other two fields, <code>colDef</code>, which lists
 * the rowset's metadata elements, and <code>data</code>, which lists the rowset's data
 * elements, are handled similarly .
 * <P>
 * This implementation of <code>XmlReaderContentHandler</code> provides the means for the
 * parser to determine which elements need to have a value set and then to set
 * those values. The methods in this class are all called by the parser; an
 * application programmer never calls them directly.
 *
 */

public class XmlReaderContentHandler extends DefaultHandler {

    private HashMap propMap;
    private HashMap colDefMap;
    private HashMap dataMap;

    private HashMap typeMap;

    private Vector updates;
    private Vector keyCols;

    private String columnValue;
    private String propertyValue;
    private String metaDataValue;

    private int tag;
    private int state;

    private WebRowSetImpl rs;
    private boolean nullVal;
    private boolean emptyStringVal;
    private RowSetMetaData md;
    private int idx;
    private String lastval;
    private String Key_map;
    private String Value_map;
    private String tempStr;
    private String tempUpdate;
    private String tempCommand;
    private Object [] upd;

    /**
     * A list of the properties for a rowset. There is a constant defined to
     * correspond to each of these properties so that a <code>HashMap</code>
     * object can be created to map the properties, which are strings, to
     * the constants, which are integers.
     */
    private String [] properties = {"command", "concurrency", "datasource",
                            "escape-processing", "fetch-direction", "fetch-size",
                            "isolation-level", "key-columns", "map",
                            "max-field-size", "max-rows", "query-timeout",
                            "read-only", "rowset-type", "show-deleted",
                            "table-name", "url", "null", "column", "type",
                            "class", "sync-provider", "sync-provider-name",
                             "sync-provider-vendor", "sync-provider-version",
                             "sync-provider-grade","data-source-lock"};

    /**
     * A constant representing the tag for the command property.
     */
    private final static int CommandTag = 0;

    /**
     * A constant representing the tag for the concurrency property.
     */
    private final static int ConcurrencyTag = 1;

    /**
     * A constant representing the tag for the datasource property.
     */
    private final static int DatasourceTag = 2;

    /**
     * A constant representing the tag for the escape-processing property.
     */
    private final static int EscapeProcessingTag = 3;

    /**
     * A constant representing the tag for the fetch-direction property.
     */
    private final static int FetchDirectionTag = 4;

    /**
     * A constant representing the tag for the fetch-size property.
     */
    private final static int FetchSizeTag = 5;

    /**
     * A constant representing the tag for the isolation-level property
     */
    private final static int IsolationLevelTag = 6;

    /**
     * A constant representing the tag for the key-columns property.
     */
    private final static int KeycolsTag = 7;

    /**
     * A constant representing the tag for the map property.
     * This map is the type map that specifies the custom mapping
     * for an SQL user-defined type.
     */
    private final static int MapTag = 8;

    /**
     * A constant representing the tag for the max-field-size property.
     */
    private final static int MaxFieldSizeTag = 9;

    /**
     * A constant representing the tag for the max-rows property.
     */
    private final static int MaxRowsTag = 10;

    /**
     * A constant representing the tag for the query-timeout property.
     */
    private final static int QueryTimeoutTag = 11;

    /**
     * A constant representing the tag for the read-only property.
     */
    private final static int ReadOnlyTag = 12;

    /**
     * A constant representing the tag for the rowset-type property.
     */
    private final static int RowsetTypeTag = 13;

    /**
     * A constant representing the tag for the show-deleted property.
     */
    private final static int ShowDeletedTag = 14;

    /**
     * A constant representing the tag for the table-name property.
     */
    private final static int TableNameTag = 15;

    /**
     * A constant representing the tag for the URL property.
     */
    private final static int UrlTag = 16;

    /**
     * A constant representing the tag for the null property.
     */
    private final static int PropNullTag = 17;

    /**
     * A constant representing the tag for the column property.
     */
    private final static int PropColumnTag = 18;

    /**
     * A constant representing the tag for the type property.
     */
    private final static int PropTypeTag = 19;

    /**
     * A constant representing the tag for the class property.
     */
    private final static int PropClassTag = 20;

    /**
     * A constant representing the tag for the sync-provider.
     */
    private final static int SyncProviderTag = 21;

    /**
     * A constant representing the tag for the sync-provider
     * name
     */
    private final static int SyncProviderNameTag = 22;

    /**
     * A constant representing the tag for the sync-provider
     * vendor tag.
     */
    private final static int SyncProviderVendorTag = 23;

    /**
     * A constant representing the tag for the sync-provider
     * version tag.
     */
    private final static int SyncProviderVersionTag = 24;

    /**
     * A constant representing the tag for the sync-provider
     * grade tag.
     */
    private final static int SyncProviderGradeTag = 25;

    /**
     * A constant representing the tag for the data source lock.
     */
    private final static int DataSourceLock = 26;

    /**
     * A listing of the kinds of metadata information available about
     * the columns in a <code>WebRowSet</code> object.
     */
    private String [] colDef = {"column-count", "column-definition", "column-index",
                        "auto-increment", "case-sensitive", "currency",
                        "nullable", "signed", "searchable",
                        "column-display-size", "column-label", "column-name",
                        "schema-name", "column-precision", "column-scale",
                        "table-name", "catalog-name", "column-type",
                        "column-type-name", "null"};


    /**
     * A constant representing the tag for column-count.
     */
    private final static int ColumnCountTag = 0;

    /**
     * A constant representing the tag for column-definition.
     */
    private final static int ColumnDefinitionTag = 1;

    /**
     * A constant representing the tag for column-index.
     */
    private final static int ColumnIndexTag = 2;

    /**
     * A constant representing the tag for auto-increment.
     */
    private final static int AutoIncrementTag = 3;

    /**
     * A constant representing the tag for case-sensitive.
     */
    private final static int CaseSensitiveTag = 4;

    /**
     * A constant representing the tag for currency.
     */
    private final static int CurrencyTag = 5;

    /**
     * A constant representing the tag for nullable.
     */
    private final static int NullableTag = 6;

    /**
     * A constant representing the tag for signed.
     */
    private final static int SignedTag = 7;

    /**
     * A constant representing the tag for searchable.
     */
    private final static int SearchableTag = 8;

    /**
     * A constant representing the tag for column-display-size.
     */
    private final static int ColumnDisplaySizeTag = 9;

    /**
     * A constant representing the tag for column-label.
     */
    private final static int ColumnLabelTag = 10;

    /**
     * A constant representing the tag for column-name.
     */
    private final static int ColumnNameTag = 11;

    /**
     * A constant representing the tag for schema-name.
     */
    private final static int SchemaNameTag = 12;

    /**
     * A constant representing the tag for column-precision.
     */
    private final static int ColumnPrecisionTag = 13;

    /**
     * A constant representing the tag for column-scale.
     */
    private final static int ColumnScaleTag = 14;

    /**
     * A constant representing the tag for table-name.
     */
    private final static int MetaTableNameTag = 15;

    /**
     * A constant representing the tag for catalog-name.
     */
    private final static int CatalogNameTag = 16;

    /**
     * A constant representing the tag for column-type.
     */
    private final static int ColumnTypeTag = 17;

    /**
     * A constant representing the tag for column-type-name.
     */
    private final static int ColumnTypeNameTag = 18;

    /**
     * A constant representing the tag for null.
     */
    private final static int MetaNullTag = 19;

    private String [] data = {"currentRow", "columnValue", "insertRow", "deleteRow", "insdel", "updateRow", "null" , "emptyString"};

    private final static int RowTag = 0;
    private final static int ColTag = 1;
    private final static int InsTag = 2;
    private final static int DelTag = 3;
    private final static int InsDelTag = 4;
    private final static int UpdTag = 5;
    private final static int NullTag = 6;
    private final static int EmptyStringTag = 7;

    /**
     * A constant indicating the state of this <code>XmlReaderContentHandler</code>
     * object in which it has not yet been called by the SAX parser and therefore
     * has no indication of what type of input to expect from the parser next.
     * <P>
     * The state is set to <code>INITIAL</code> at the end of each
     * section, which allows the sections to appear in any order and
     * still be parsed correctly (except that metadata must be
     * set before data values can be set).
     */
    private final static int INITIAL = 0;

    /**
     * A constant indicating the state in which this <code>XmlReaderContentHandler</code>
     * object expects the next input received from the
     * SAX parser to be a string corresponding to one of the elements in
     * <code>properties</code>.
     */
    private final static int PROPERTIES = 1;

    /**
     * A constant indicating the state in which this <code>XmlReaderContentHandler</code>
     * object expects the next input received from the
     * SAX parser to be a string corresponding to one of the elements in
     * <code>colDef</code>.
     */
    private final static int METADATA = 2;

    /**
     * A constant indicating the state in which this <code>XmlReaderContentHandler</code>
     * object expects the next input received from the
     * SAX parser to be a string corresponding to one of the elements in
     * <code>data</code>.
     */
    private final static int DATA = 3;

    private  JdbcRowSetResourceBundle resBundle;

    /**
     * Constructs a new <code>XmlReaderContentHandler</code> object that will
     * assist the SAX parser in reading a <code>WebRowSet</code> object in the
     * format of an XML document. In addition to setting some default values,
     * this constructor creates three <code>HashMap</code> objects, one for
     * properties, one for metadata, and one for data.  These hash maps map the
     * strings sent by the SAX parser to integer constants so that they can be
     * compared more efficiently in <code>switch</code> statements.
     *
     * @param r the <code>RowSet</code> object in XML format that will be read
     */
    public XmlReaderContentHandler(RowSet r) {
        // keep the rowset we've been given
        rs = (WebRowSetImpl)r;

        // set-up the token maps
        initMaps();

        // allocate the collection for the updates
        updates = new Vector();

        // start out with the empty string
        columnValue = new String("");
        propertyValue = new String("");
        metaDataValue = new String("");

        nullVal = false;
        idx = 0;
        tempStr = "";
        tempUpdate = "";
        tempCommand = "";

        try {
           resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
        } catch(IOException ioe) {
            throw new RuntimeException(ioe);
        }
    }

    /**
     * Creates and initializes three new <code>HashMap</code> objects that map
     * the strings returned by the SAX parser to <code>Integer</code>
     * objects.  The strings returned by the parser will match the strings that
     * are array elements in this <code>XmlReaderContentHandler</code> object's
     * <code>properties</code>, <code>colDef</code>, or <code>data</code>
     * fields. For each array element in these fields, there is a corresponding
     * constant defined. It is to these constants that the strings are mapped.
     * In the <code>HashMap</code> objects, the string is the key, and the
     * integer is the value.
     * <P>
     * The purpose of the mapping is to make comparisons faster.  Because comparing
     * numbers is more efficient than comparing strings, the strings returned
     * by the parser are mapped to integers, which can then be used in a
     * <code>switch</code> statement.
     */
    private void initMaps() {
        int items, i;

        propMap = new HashMap();
        items = properties.length;

        for (i=0;i<items;i++) {
            propMap.put(properties[i], new Integer(i));
        }

        colDefMap = new HashMap();
        items = colDef.length;

        for (i=0;i<items;i++) {
            colDefMap.put(colDef[i], new Integer(i));
        }

        dataMap = new HashMap();
        items = data.length;

        for (i=0;i<items;i++) {
            dataMap.put(data[i], new Integer(i));
        }

        //Initialize connection map here
        typeMap = new HashMap();
    }

    public void startDocument() throws SAXException {
    }

    public void endDocument() throws SAXException {
    }


    /**
     * Sets this <code>XmlReaderContentHandler</code> object's <code>tag</code>
     * field if the given name is the key for a tag and this object's state
     * is not <code>INITIAL</code>.  The field is set
     * to the constant that corresponds to the given element name.
     * If the state is <code>INITIAL</code>, the state is set to the given
     * name, which will be one of the sections <code>PROPERTIES</code>,
     * <code>METADATA</code>, or <code>DATA</code>.  In either case, this
     * method puts this document handler in the proper state for calling
     * the method <code>endElement</code>.
     * <P>
     * If the state is <code>DATA</code> and the tag is <code>RowTag</code>,
     * <code>DelTag</code>, or <code>InsTag</code>, this method moves the
     * rowset's cursor to the insert row and sets this
     * <code>XmlReaderContentHandler</code> object's <code>idx</code>
     * field to <code>0</code> so that it will be in the proper
     * state when the parser calls the method <code>endElement</code>.
     *
     * @param lName the name of the element; either (1) one of the array
     *        elements in the fields <code>properties</code>,
     *        <code>colDef</code>, or <code>data</code> or
     *        (2) one of the <code>RowSet</code> elements
     *        <code>"properties"</code>, <code>"metadata"</code>, or
     *        <code>"data"</code>
     * @param attributes <code>org.xml.sax.AttributeList</code> objects that are
     *             attributes of the named section element; may be <code>null</code>
     *             if there are no attributes, which is the case for
     *             <code>WebRowSet</code> objects
     * @exception SAXException if a general SAX error occurs
     */
    public void startElement(String uri, String lName, String qName, Attributes attributes) throws SAXException {
        int tag;
        String name = "";

        name = lName;

        switch (getState()) {
        case PROPERTIES:

            tempCommand = "";
            tag = ((Integer)propMap.get(name)).intValue();
            if (tag == PropNullTag)
               setNullValue(true);
            else
                setTag(tag);
            break;
        case METADATA:
            tag = ((Integer)colDefMap.get(name)).intValue();

            if (tag == MetaNullTag)
                setNullValue(true);
            else
                setTag(tag);
            break;
        case DATA:

            /**
              * This has been added to clear out the values of the previous read
              * so that we should not add up values of data between different tags
              */
            tempStr = "";
            tempUpdate = "";
            if(dataMap.get(name) == null) {
                tag = NullTag;
            } else if(((Integer)dataMap.get(name)).intValue() == EmptyStringTag) {
                tag = EmptyStringTag;
            } else {
                 tag = ((Integer)dataMap.get(name)).intValue();
            }

            if (tag == NullTag) {
                setNullValue(true);
            } else if(tag == EmptyStringTag) {
                setEmptyStringValue(true);
            } else {
                setTag(tag);

                if (tag == RowTag || tag == DelTag || tag == InsTag) {
                    idx = 0;
                    try {
                        rs.moveToInsertRow();
                    } catch (SQLException ex) {
                        ;
                    }
                }
            }

            break;
        default:
            setState(name);
        }

    }

    /**
     * Sets the value for the given element if <code>name</code> is one of
     * the array elements in the fields <code>properties</code>,
     * <code>colDef</code>, or <code>data</code> and this
     * <code>XmlReaderContentHandler</code> object's state is not
     * <code>INITIAL</code>. If the state is <code>INITIAL</code>,
     * this method does nothing.
     * <P>
     * If the state is <code>METADATA</code> and
     * the argument supplied is <code>"metadata"</code>, the rowset's
     * metadata is set. If the state is <code>PROPERTIES</code>, the
     * appropriate property is set using the given name to determine
     * the appropriate value. If the state is <code>DATA</code> and
     * the argument supplied is <code>"data"</code>, this method sets
     * the state to <code>INITIAL</code> and returns.  If the argument
     * supplied is one of the elements in the field <code>data</code>,
     * this method makes the appropriate changes to the rowset's data.
     *
     * @param lName the name of the element; either (1) one of the array
     *        elements in the fields <code>properties</code>,
     *        <code>colDef</code>, or <code>data</code> or
     *        (2) one of the <code>RowSet</code> elements
     *        <code>"properties"</code>, <code>"metadata"</code>, or
     *        <code>"data"</code>
     *
     * @exception SAXException if a general SAX error occurs
     */
    public void endElement(String uri, String lName, String qName) throws SAXException {
        int tag;

        String name = "";
        name = lName;

        switch (getState()) {
        case PROPERTIES:
            if (name.equals("properties")) {
                state = INITIAL;
                break;
            }

            try {
                tag = ((Integer)propMap.get(name)).intValue();
                switch (tag) {
                case KeycolsTag:
                    if (keyCols != null) {
                        int i[] = new int[keyCols.size()];
                        for (int j = 0; j < i.length; j++)
                            i[j] = Integer.parseInt((String)keyCols.elementAt(j));
                        rs.setKeyColumns(i);
                    }
                    break;

                 case PropClassTag:
                     //Added the handling for Class tags to take care of maps
                     //Makes an entry into the map upon end of class tag
                     try{
                          typeMap.put(Key_map,Class.forName(Value_map));

                        }catch(ClassNotFoundException ex) {
                          throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errmap").toString(), ex.getMessage()));
                        }
                      break;

                 case MapTag:
                      //Added the handling for Map to take set the typeMap
                      rs.setTypeMap(typeMap);
                      break;

                default:
                    break;
                }

                if (getNullValue()) {
                    setPropertyValue(null);
                    setNullValue(false);
                } else {
                    setPropertyValue(propertyValue);
                }
            } catch (SQLException ex) {
                throw new SAXException(ex.getMessage());
            }

            // propertyValue need to be reset to an empty string
            propertyValue = new String("");
            setTag(-1);
            break;
        case METADATA:
            if (name.equals("metadata")) {
                try {
                    rs.setMetaData(md);
                    state = INITIAL;
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errmetadata").toString(), ex.getMessage()));
                }
            } else {
                try {
                    if (getNullValue()) {
                        setMetaDataValue(null);
                        setNullValue(false);
                    } else {
                        setMetaDataValue(metaDataValue);
                    }
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errmetadata").toString(), ex.getMessage()));

                }
                // metaDataValue needs to be reset to an empty string
                metaDataValue = new String("");
            }
            setTag(-1);
            break;
        case DATA:
            if (name.equals("data")) {
                state = INITIAL;
                return;
            }

            if(dataMap.get(name) == null) {
                tag = NullTag;
            } else {
                 tag = ((Integer)dataMap.get(name)).intValue();
            }
            switch (tag) {
            case ColTag:
                try {
                    idx++;
                    if (getNullValue()) {
                        insertValue(null);
                        setNullValue(false);
                    } else {
                        insertValue(tempStr);
                    }
                    // columnValue now need to be reset to the empty string
                    columnValue = new String("");
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errinsert").toString(), ex.getMessage()));
                }
                break;
            case RowTag:
                try {
                    rs.insertRow();
                    rs.moveToCurrentRow();
                    rs.next();

                    // Making this as the original to turn off the
                    // rowInserted flagging
                    rs.setOriginalRow();

                    applyUpdates();
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errconstr").toString(), ex.getMessage()));
                }
                break;
            case DelTag:
                try {
                    rs.insertRow();
                    rs.moveToCurrentRow();
                    rs.next();
                    rs.setOriginalRow();
                    applyUpdates();
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errdel").toString() , ex.getMessage()));
                }
                break;
            case InsTag:
                try {
                    rs.insertRow();
                    rs.moveToCurrentRow();
                    rs.next();
                    applyUpdates();
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errinsert").toString() , ex.getMessage()));
                }
                break;

            case InsDelTag:
                try {
                    rs.insertRow();
                    rs.moveToCurrentRow();
                    rs.next();
                    rs.setOriginalRow();
                    applyUpdates();
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errinsdel").toString() , ex.getMessage()));
                }
                break;

             case UpdTag:
                 try {
                        if(getNullValue())
                         {
                          insertValue(null);
                          setNullValue(false);
                         } else if(getEmptyStringValue()) {
                               insertValue("");
                               setEmptyStringValue(false);
                         } else {
                            updates.add(upd);
                         }
                 }  catch(SQLException ex) {
                        throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errupdate").toString() , ex.getMessage()));
                 }
                break;

            default:
                break;
            }
        default:
            break;
        }
    }

    private void applyUpdates() throws SAXException {
        // now handle any updates
        if (updates.size() > 0) {
            try {
                Object upd[];
                Iterator i = updates.iterator();
                while (i.hasNext()) {
                    upd = (Object [])i.next();
                    idx = ((Integer)upd[0]).intValue();

                   if(!(lastval.equals(upd[1]))){
                       insertValue((String)(upd[1]));
                    }
                }

                rs.updateRow();
                } catch (SQLException ex) {
                    throw new SAXException(MessageFormat.format(resBundle.handleGetObject("xmlrch.errupdrow").toString() , ex.getMessage()));
                }
            updates.removeAllElements();
        }


    }

    /**
     * Sets a property, metadata, or data value with the characters in
     * the given array of characters, starting with the array element
     * indicated by <code>start</code> and continuing for <code>length</code>
     * number of characters.
     * <P>
     * The SAX parser invokes this method and supplies
     * the character array, start position, and length parameter values it
     * got from parsing the XML document.  An application programmer never
     * invokes this method directly.
     *
     * @param ch an array of characters supplied by the SAX parser, all or part of
     *         which will be used to set a value
     * @param start the position in the given array at which to start
     * @param length the number of consecutive characters to use
     */
    public void characters(char[] ch, int start, int length) throws SAXException {
        try {
            switch (getState()) {
            case PROPERTIES:
                propertyValue = new String(ch, start, length);

                /**
                  * This has been added for handling of special characters. When special
                  * characters are encountered the characters function gets called for
                  * each of the characters so we need to append the value got in the
                  * previous call as it is the same data present between the start and
                  * the end tag.
                  **/
                tempCommand = tempCommand.concat(propertyValue);
                propertyValue = tempCommand;

                // Added the following check for handling of type tags in maps
                if(tag == PropTypeTag)
                {
                        Key_map = propertyValue;
                }

                // Added the following check for handling of class tags in maps
                else if(tag == PropClassTag)
                {
                        Value_map = propertyValue;
                }
                break;

            case METADATA:

                // The parser will come here after the endElement as there is
                // "\n" in the after endTag is printed. This will cause a problem
                // when the data between the tags is an empty string so adding
                // below condition to take care of that situation.

                if (tag == -1)
                {
                        break;
                }

                metaDataValue = new String(ch, start, length);
                break;
            case DATA:
                setDataValue(ch, start, length);
                break;
            default:
                ;
            }
        } catch (SQLException ex) {
            throw new SAXException(resBundle.handleGetObject("xmlrch.chars").toString() + ex.getMessage());
        }
    }

    private void setState(String s) throws SAXException {
        if (s.equals("webRowSet")) {
            state = INITIAL;
        } else if (s.equals("properties")) {
            if (state != PROPERTIES)
                state = PROPERTIES;
            else
                state = INITIAL;
        } else if (s.equals("metadata")) {
            if (state != METADATA)
                state = METADATA;
            else
                state = INITIAL;
        } else if (s.equals("data")) {
            if (state != DATA)
                state = DATA;
            else
                state = INITIAL;
        }

    }

    /**
     * Retrieves the current state of this <code>XmlReaderContentHandler</code>
     * object's rowset, which is stored in the document handler's
     * <code>state</code> field.
     *
     * @return one of the following constants:
     *         <code>XmlReaderContentHandler.PROPERTIES</code>
     *         <code>XmlReaderContentHandler.METADATA</code>
     *         <code>XmlReaderContentHandler.DATA</code>
     *         <code>XmlReaderContentHandler.INITIAL</code>
     */
    private int getState() {
        return state;
    }

    private void setTag(int t) {
        tag = t;
    }

    private int getTag() {
        return tag;
    }

    private void setNullValue(boolean n) {
        nullVal = n;
    }

    private boolean getNullValue() {
        return nullVal;
    }

    private void setEmptyStringValue(boolean e) {
        emptyStringVal = e;
    }

    private boolean getEmptyStringValue() {
        return emptyStringVal;
    }

    private String getStringValue(String s) {
         return s;
    }

    private int getIntegerValue(String s) {
        return Integer.parseInt(s);
    }

    private boolean getBooleanValue(String s) {

        return new Boolean(s).booleanValue();
    }

    private java.math.BigDecimal getBigDecimalValue(String s) {
        return new java.math.BigDecimal(s);
    }

    private byte getByteValue(String s) {
        return Byte.parseByte(s);
    }

    private short getShortValue(String s) {
        return Short.parseShort(s);
    }

    private long getLongValue(String s) {
        return Long.parseLong(s);
    }

    private float getFloatValue(String s) {
        return Float.parseFloat(s);
    }

    private double getDoubleValue(String s) {
        return Double.parseDouble(s);
    }

    private byte[] getBinaryValue(String s) {
        return s.getBytes();
    }

    private java.sql.Date getDateValue(String s) {
        return new java.sql.Date(getLongValue(s));
    }

    private java.sql.Time getTimeValue(String s) {
        return new java.sql.Time(getLongValue(s));
    }

    private java.sql.Timestamp getTimestampValue(String s) {
        return new java.sql.Timestamp(getLongValue(s));
    }

    private void setPropertyValue(String s) throws SQLException {
        // find out if we are going to be dealing with a null
        boolean nullValue = getNullValue();

        switch(getTag()) {
        case CommandTag:
            if (nullValue)
               ; //rs.setCommand(null);
            else
                rs.setCommand(s);
            break;
        case ConcurrencyTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setConcurrency(getIntegerValue(s));
            break;
        case DatasourceTag:
            if (nullValue)
                rs.setDataSourceName(null);
            else
                rs.setDataSourceName(s);
            break;
        case EscapeProcessingTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setEscapeProcessing(getBooleanValue(s));
            break;
        case FetchDirectionTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setFetchDirection(getIntegerValue(s));
            break;
        case FetchSizeTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setFetchSize(getIntegerValue(s));
            break;
        case IsolationLevelTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setTransactionIsolation(getIntegerValue(s));
            break;
        case KeycolsTag:
            break;
        case PropColumnTag:
            if (keyCols == null)
                keyCols = new Vector();
            keyCols.add(s);
            break;
        case MapTag:
            break;
        case MaxFieldSizeTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setMaxFieldSize(getIntegerValue(s));
            break;
        case MaxRowsTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setMaxRows(getIntegerValue(s));
            break;
        case QueryTimeoutTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setQueryTimeout(getIntegerValue(s));
            break;
        case ReadOnlyTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setReadOnly(getBooleanValue(s));
            break;
        case RowsetTypeTag:
            if (nullValue) {
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            } else {
                //rs.setType(getIntegerValue(s));
                String strType = getStringValue(s);
                int iType = 0;

                if(strType.trim().equals("ResultSet.TYPE_SCROLL_INSENSITIVE")) {
                   iType = 1004;
                } else if(strType.trim().equals("ResultSet.TYPE_SCROLL_SENSITIVE"))   {
                   iType = 1005;
                } else if(strType.trim().equals("ResultSet.TYPE_FORWARD_ONLY")) {
                   iType = 1003;
                }
                rs.setType(iType);
            }
            break;
        case ShowDeletedTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue").toString());
            else
                rs.setShowDeleted(getBooleanValue(s));
            break;
        case TableNameTag:
            if (nullValue)
                //rs.setTableName(null);
                ;
            else
                rs.setTableName(s);
            break;
        case UrlTag:
            if (nullValue)
                rs.setUrl(null);
            else
                rs.setUrl(s);
            break;
        case SyncProviderNameTag:
            if (nullValue) {
                rs.setSyncProvider(null);
            } else {
                String str = s.substring(0,s.indexOf("@")+1);
                rs.setSyncProvider(str);
            }
            break;
        case SyncProviderVendorTag:
            // to be implemented
            break;
        case SyncProviderVersionTag:
            // to be implemented
            break;
        case SyncProviderGradeTag:
            // to be implemented
            break;
        case DataSourceLock:
            // to be implemented
            break;
        default:
            break;
        }

    }

    private void setMetaDataValue(String s) throws SQLException {
        // find out if we are going to be dealing with a null
        boolean nullValue = getNullValue();

        switch (getTag()) {
        case ColumnCountTag:
            md = new RowSetMetaDataImpl();
            idx = 0;

            if (nullValue) {
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            } else {
                md.setColumnCount(getIntegerValue(s));
            }
            break;
        case ColumnDefinitionTag:
            break;
        case ColumnIndexTag:
            idx++;
            break;
        case AutoIncrementTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setAutoIncrement(idx, getBooleanValue(s));
            break;
        case CaseSensitiveTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setCaseSensitive(idx, getBooleanValue(s));
            break;
        case CurrencyTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setCurrency(idx, getBooleanValue(s));
            break;
        case NullableTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setNullable(idx, getIntegerValue(s));
            break;
        case SignedTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setSigned(idx, getBooleanValue(s));
            break;
        case SearchableTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setSearchable(idx, getBooleanValue(s));
            break;
        case ColumnDisplaySizeTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setColumnDisplaySize(idx, getIntegerValue(s));
            break;
        case ColumnLabelTag:
            if (nullValue)
                md.setColumnLabel(idx, null);
            else
                md.setColumnLabel(idx, s);
            break;
        case ColumnNameTag:
            if (nullValue)
                md.setColumnName(idx, null);
            else
                md.setColumnName(idx, s);

            break;
        case SchemaNameTag:
            if (nullValue) {
                md.setSchemaName(idx, null); }
            else
                md.setSchemaName(idx, s);
            break;
        case ColumnPrecisionTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setPrecision(idx, getIntegerValue(s));
            break;
        case ColumnScaleTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setScale(idx, getIntegerValue(s));
            break;
        case MetaTableNameTag:
            if (nullValue)
                md.setTableName(idx, null);
            else
                md.setTableName(idx, s);
            break;
        case CatalogNameTag:
            if (nullValue)
                md.setCatalogName(idx, null);
            else
                md.setCatalogName(idx, s);
            break;
        case ColumnTypeTag:
            if (nullValue)
                throw new SQLException(resBundle.handleGetObject("xmlrch.badvalue1").toString());
            else
                md.setColumnType(idx, getIntegerValue(s));
            break;
        case ColumnTypeNameTag:
            if (nullValue)
                md.setColumnTypeName(idx, null);
            else
                md.setColumnTypeName(idx, s);
            break;
        default:
            //System.out.println("MetaData: Unknown Tag: (" + getTag() + ")");
            break;

        }
    }

    private void setDataValue(char[] ch, int start, int len) throws SQLException {
        switch (getTag()) {
        case ColTag:
            columnValue = new String(ch, start, len);
            /**
              * This has been added for handling of special characters. When special
              * characters are encountered the characters function gets called for
              * each of the characters so we need to append the value got in the
              * previous call as it is the same data present between the start and
              * the end tag.
              **/
            tempStr = tempStr.concat(columnValue);
            break;
        case UpdTag:
            upd = new Object[2];

            /**
              * This has been added for handling of special characters. When special
              * characters are encountered the characters function gets called for
              * each of the characters so we need to append the value got in the
              * previous call as it is the same data present between the start and
              * the end tag.
              **/

            tempUpdate = tempUpdate.concat(new String(ch,start,len));
            upd[0] = new Integer(idx);
            upd[1] = tempUpdate;
            //updates.add(upd);

            lastval = (String)upd[1];
            //insertValue(ch, start, len);
            break;
        case InsTag:

        }
    }

    private void insertValue(String s) throws SQLException {

        if (getNullValue()) {
            rs.updateNull(idx);
            return;
        }

        // no longer have to deal with those pesky nulls.
        int type = rs.getMetaData().getColumnType(idx);
        switch (type) {
        case java.sql.Types.BIT:
            rs.updateBoolean(idx, getBooleanValue(s));
            break;
        case java.sql.Types.BOOLEAN:
            rs.updateBoolean(idx, getBooleanValue(s));
            break;
        case java.sql.Types.SMALLINT:
        case java.sql.Types.TINYINT:
            rs.updateShort(idx, getShortValue(s));
            break;
        case java.sql.Types.INTEGER:
            rs.updateInt(idx, getIntegerValue(s));
            break;
        case java.sql.Types.BIGINT:
            rs.updateLong(idx, getLongValue(s));
            break;
        case java.sql.Types.REAL:
        case java.sql.Types.FLOAT:
            rs.updateFloat(idx, getFloatValue(s));
            break;
        case java.sql.Types.DOUBLE:
            rs.updateDouble(idx, getDoubleValue(s));
            break;
        case java.sql.Types.NUMERIC:
        case java.sql.Types.DECIMAL:
            rs.updateObject(idx, getBigDecimalValue(s));
            break;
        case java.sql.Types.BINARY:
        case java.sql.Types.VARBINARY:
        case java.sql.Types.LONGVARBINARY:
            rs.updateBytes(idx, getBinaryValue(s));
            break;
        case java.sql.Types.DATE:
            rs.updateDate(idx,  getDateValue(s));
            break;
        case java.sql.Types.TIME:
            rs.updateTime(idx, getTimeValue(s));
            break;
        case java.sql.Types.TIMESTAMP:
            rs.updateTimestamp(idx, getTimestampValue(s));
            break;
        case java.sql.Types.CHAR:
        case java.sql.Types.VARCHAR:
        case java.sql.Types.LONGVARCHAR:
            rs.updateString(idx, getStringValue(s));
            break;
        default:

        }

    }

    /**
     * Throws the given <code>SAXParseException</code> object. This
     * exception was originally thrown by the SAX parser and is passed
     * to the method <code>error</code> when the SAX parser invokes it.
     *
     * @param e the <code>SAXParseException</code> object to throw
     */
    public void error (SAXParseException e) throws SAXParseException {
            throw e;
    }

    // dump warnings too
    /**
     * Prints a warning message to <code>System.out</code> giving the line
     * number and uri for what caused the warning plus a message explaining
     * the reason for the warning. This method is invoked by the SAX parser.
     *
     * @param err a warning generated by the SAX parser
     */
    public void warning (SAXParseException err) throws SAXParseException {
        System.out.println (MessageFormat.format(resBundle.handleGetObject("xmlrch.warning").toString(), new Object[] { err.getMessage(), err.getLineNumber(), err.getSystemId() }));
    }

    /**
     *
     */
    public void notationDecl(String name, String publicId, String systemId) {

    }

    /**
     *
     */
    public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) {

    }

   /**
    * Returns the current row of this <code>Rowset</code>object.
    * The ResultSet's cursor is positioned at the Row which is needed
    *
    * @return the <code>Row</code> object on which the <code>RowSet</code>
    *           implementation objects's cursor is positioned
    */
    private Row getPresentRow(WebRowSetImpl rs) throws SQLException {
         //rs.setOriginalRow();
         // ResultSetMetaData rsmd = rs.getMetaData();
         // int numCols = rsmd.getColumnCount();
         // Object vals[] = new Object[numCols];
         // for(int j = 1; j<= numCols ; j++){
         //     vals[j-1] = rs.getObject(j);
         // }
         // return(new Row(numCols, vals));
         return null;
   }




}
