/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* $Rev$ $Date$ */

#include "commonj/sdo/SDOSAX2Parser.h"

#include "commonj/sdo/SDORuntimeException.h"
#include "commonj/sdo/ChangeSummary.h"
#include "commonj/sdo/XSDPropertyInfo.h"
#include "commonj/sdo/XMLQName.h"
#include "commonj/sdo/DASProperty.h"
#include "commonj/sdo/Logging.h"
#include "commonj/sdo/DASType.h"
#include "commonj/sdo/XSDTypeInfo.h"
#include "commonj/sdo/TypeImpl.h"
#include "commonj/sdo/DataObjectImpl.h"
#include "commonj/sdo/DataFactoryImpl.h"
#include "commonj/sdo/SDOUtils.h"
#include <stdio.h>
using namespace std;

namespace commonj
{
    namespace sdo
    {
     
      using internal::XSDPropertyInfo;
      using internal::PropertyDefinitionImpl;
      using internal::XSDTypeInfo;
      using internal::TypeDefinitionImpl;
      using internal::SDOUtils;

        SDOSAX2Parser::SDOSAX2Parser(
            DataFactoryPtr df,
            const SDOXMLString& targetNamespace,
            DataObjectPtr& rootDO,
            ParserErrorSetter* insetter
            ) 
            
            : dataFactory(staticCast<DataFactoryImplPtr>(df)),
            targetNamespaceURI(targetNamespace),
            rootDataObject(rootDO),
            currentDataObject(0),
            isDataGraph(false),
            ignoreEvents(false),
            dealingWithChangeSummary(false),
            csbuilder(0),
            SAX2Parser(insetter),
            rootElementName(""),
            rootElementURI("")
            
    
        {
            reset();
            if (targetNamespace.isNull())
            {
                targetNamespaceURI = "";
            }
            rootDataObject = 0;
            newSequence = true;
        }
        
        SDOSAX2Parser::~SDOSAX2Parser()
        {
        }
        
        void SDOSAX2Parser::reset()
        {
            rootDataObject = 0;
            currentDataObject = 0;
            isDataGraph = false;
            ignoreEvents = false;
            changeSummary = false;
            IDMap.empty();
            IDRefs.empty();
            rootElementURI = "";
            rootElementName = "";
        }
        
        void SDOSAX2Parser::setRootElementName(const SDOXMLString& name)
        {
            rootElementName = name;
        }

        void SDOSAX2Parser::setRootElementURI(const SDOXMLString& uri)
        {
            rootElementURI = uri;
        }
       
        void SDOSAX2Parser::startDocument()
        {
            LOGINFO(INFO,"SDOSAX2Parser: startDocument");
            setNamespaces = true;
            reset();
        }
        
        void SDOSAX2Parser::endDocument()
        {
            LOGENTRY(INFO,"SDOSAX2Parser: endDocument");
            // Iterate over IDREFs list and set references
            ID_REFS::iterator refsIter;
            for (refsIter = IDRefs.begin(); refsIter != IDRefs.end(); refsIter++)
            {
                try
                {
                    const Type& type = refsIter->dataObject->getType();
                    PropertyPtr prop  = refsIter->dataObject->getInstanceProperty((const char*)refsIter->property);
                    if (!prop) {
                      SDOString msg("Cannot find property: ");
                      msg += (const char*)refsIter->property;
                      throw SDOPropertyNotFoundException(TUSCANY_SDO_EINFO,
                                                         msg.c_str());
                    }
                    const Type& propType =    ((TypeImpl&)type).getRealPropertyType(refsIter->property);

                    // Allowing referenes to DataObjects only
                    if (!propType.isDataType())
                    {
                        DataObjectPtr reffedDO;
                        ID_MAP::iterator idIter = IDMap.find(refsIter->value);
                        if (idIter != IDMap.end())
                        {
                            reffedDO = idIter->second;
                        }
                        else
                        {
                            // assume it is an XPath?

                            // Remove #/ from front of XPATH as getDataObject doeesnt
                            // support this yet - it does now
                            //SDOXMLString xpath(refsIter->value);
                            //if (xpath.firstIndexOf('#') == 0)
                            //    xpath = xpath.substring(1);
                            //if (xpath.firstIndexOf('/') == 0)
                            //    xpath = xpath.substring(1);

                            reffedDO = rootDataObject->getDataObject((const char*)refsIter->value);
                        }

                        if (!reffedDO)
                        {
                            continue;
                        }

                        if (prop->isMany())
                        {
                            DataObjectList& dol = refsIter->dataObject->getList(*prop);
                            dol.append(reffedDO);
                        }
                        else
                        {
                            refsIter->dataObject->setDataObject(*prop, reffedDO); 
                        }
                    }

                }
                catch (const SDORuntimeException&)
                {
                }
            }
            try {
                // Now rebuild the changeSummary
                if (csbuilder != 0)
                {
                    csbuilder->buildChangeSummary(changeSummaryDO);
                    delete csbuilder;
                    csbuilder = 0;
                }
            }
            catch (SDORuntimeException&)
            {
            }

            LOGEXIT(INFO,"SDOSAX2Parser: endDocument");
        }


        bool SDOSAX2Parser::setDO(DataObjectPtr newDO, 
                                  SDOXMLString& propertyName)
        {
            LOGENTRY(INFO,"SDOSAX2Parser: setDO");

            if (currentDataObject)
            {
                const Type& type = currentDataObject->getType();
                // go lower level so we can find open properties w/o exception
                DataObject* dob = getRawPointer(currentDataObject);
                const PropertyImpl* pprop = ((DataObjectImpl*)dob)->getPropertyImpl((const char*)propertyName);
                if (pprop == 0)
                {

                    LOGEXIT(INFO,"SDOSAX2Parser: setDO - exit1");
                    return false;
                }

                const Property& property = (Property&)*pprop;
                //const Type& propertyType = ((TypeImpl&)type).getRealPropertyType(propertyName);
                if (currentDataObject->getType().isSequencedType())
                {
                    SequencePtr seq = currentDataObject->getSequence();
                    seq->addDataObject(property, newDO);
                }
                else
                {
                    if (!property.isMany())
                    {
                        currentDataObject->setDataObject((const char*)propertyName, newDO);
                    }
                    else
                    {
                        DataObjectList& dol = currentDataObject->getList((const char*)propertyName);
                        dol.append(newDO);
                    }
                }
            }
            
            setCurrentDataObject(newDO);

            LOGEXIT(INFO,"SDOSAX2Parser: setDO - exit2");
            return true;
        }
                    
        void SDOSAX2Parser::handleOpenAttribute(
                                    SDOXMLString& tns,
                                    const SDOXMLString& propuri,
                                    const SDOXMLString& propname,
                                    const SDOXMLString& value)
        {
            // first, see if there is a global element or attribute corresponding...
            try 
            {
                const PropertyImpl* prop = 0;
                const TypeImpl* ti = 
                    dataFactory->findTypeImpl(propuri,"RootType");
                
                if (ti != 0)
                {
                    prop = (const PropertyImpl*)ti->getPropertyImpl(propname);
                }
                else
                {
                    ti = dataFactory->findTypeImpl(tns,"RootType");
                }

                if (ti != 0)
                {
                    prop = (const PropertyImpl*)ti->getPropertyImpl(propname);
                }
            
                if (prop == 0)
                {
                    // need to use the sequence interface if it exists
                    if (currentDataObject->getType().isSequencedType())
                    {
                        SequencePtr seq = currentDataObject->getSequence();
                        seq->addCString(propname, value);
                    }
                    else
                    {
                        currentDataObject->setCString((const char*)propname,value);
                    }
                    return;
                }

                DataObject* dob = getRawPointer(currentDataObject);

                switch (prop->getTypeEnum())
                {
                    case Type::BooleanType:
                        ((DataObjectImpl*)dob)->defineBoolean(propname);
                        break;
                    case Type::ByteType:
                        ((DataObjectImpl*)dob)->defineByte(propname);
                        break;
                    case Type::CharacterType:
                        ((DataObjectImpl*)dob)->defineCharacter(propname);
                        break;
                    case Type::BytesType:
                        ((DataObjectImpl*)dob)->defineBytes(propname);
                        break;
                    case Type::StringType:
                        ((DataObjectImpl*)dob)->defineString(propname);
                        break;
                    case Type::ShortType:
                        ((DataObjectImpl*)dob)->defineShort(propname);
                        break;
                    case Type::IntType:
                        ((DataObjectImpl*)dob)->defineInt(propname);
                        break;
                    case Type::LongType:
                        ((DataObjectImpl*)dob)->defineLong(propname);
                        break;
                    case Type::DoubleType:
                        ((DataObjectImpl*)dob)->defineDouble(propname);
                        break;
                    case Type::FloatType:
                        ((DataObjectImpl*)dob)->defineFloat(propname);
                        break;
                    case Type::DateType:
                        ((DataObjectImpl*)dob)->defineDate(propname);
                        break;
                } // switch

                // regardless of what type the property now is, we can set CString , and the
                // right conversion will happen

                // need to use the sequence interface if it exists.
                if (currentDataObject->getType().isSequencedType())
                {
                    SequencePtr seq = currentDataObject->getSequence();
                    seq->addCString(propname, value);
                }
                else
                {
                    currentDataObject->setCString((const char*)propname,value);
                }
            }
            catch (SDORuntimeException)
            {
            }
            return;
        }


        void SDOSAX2Parser::setAttributes(
            SDOXMLString& tns,
            const SAX2Namespaces& namespaces,
            const SAX2Attributes& attributes) 
        {

            LOGENTRY(INFO,"SDOSAX2Parser::setAttributes");

            //////////////////////////////////////////////
            // The attributes are properties on the new DO
            // Handle attributes
            //////////////////////////////////////////////
            for (int i=0; i < attributes.size(); i++)
            {
                // Should ignore attributes like xsi:type
                if (!(attributes[i].getUri().equalsIgnoreCase("http://www.w3.org/2001/XMLSchema-instance")))
                {                            
                    try
                    {
                        const SDOXMLString& propertyName = getSDOName(*currentDataObjectType, attributes[i].getName());
                        DataObject* dob = getRawPointer(currentDataObject);
                        const PropertyImpl* pprop = ((DataObjectImpl*)dob)->getPropertyImpl(propertyName);
                        if (pprop == 0 )
                        {
                            if (currentDataObject->getType().isOpenType())
                            {
                                // if its an open type, then attributes will be allowed to have 
                                // an invalid name, and setCString will create them all as bytes
                                handleOpenAttribute(tns, attributes[i].getUri(),
                                                            attributes[i].getName(),
                                                    attributes[i].getValue());
                                
                            }
                            else 
                            {
                                LOGERROR_1(WARNING,"SDOSAX2Parser: Property not found on closed type (ignored):%s",
                                    (const char*)(attributes[i].getName()));        
                            }
                        }
                        else 
                        {
                            const Property& prop = (Property&)*pprop;
                            SDOXMLString propValue;
                            
                            XSDPropertyInfo* pi = (XSDPropertyInfo*)((DASProperty*)&prop)->getDASValue("XMLDAS::PropertyInfo");
                            if (pi && pi->getPropertyDefinition().isElement)
                            {
                                // xml instance is trying to set an attribute when schema defines property as element
                                LOGERROR_1(WARNING,"SDOSAX2Parser: Attribute %s should be an element. Attribute ignored",
                                 (const char*)(attributes[i].getName()));        
                                continue;
                            }

                            if (pi && pi->getPropertyDefinition().isQName)
                            {
                                XMLQName qname(attributes[i].getValue(),
                                    documentNamespaces, namespaces);
                                propValue = qname.getSDOName();
                            }
                            else
                            {
                                propValue = attributes[i].getValue();
                            }
                    
                            if ((pi && pi->getPropertyDefinition().isIDREF)
                                || prop.isReference())
                            {
                                // remember this value to resolve later
                                IDRef ref(currentDataObject, attributes[i].getName(), propValue);
                                IDRefs.push_back(ref);
                            }
                            else
                            {    
                                if (pi && pi->getPropertyDefinition().isID)
                                {
                                    // add this ID to the map
                                    IDMap[propValue] = currentDataObject;
                                }
                                // Always set the property as a String. SDO will do the conversion
                                currentDataObject->setCString((const char*)attributes[i].getName(), propValue);
                            }
                        }
                    }
                    catch (const SDOPropertyNotFoundException&)
                    {
                        LOGERROR_1(WARNING,"SDOSAX2Parser: Error processing attribute (ignored):%s",
                            (const char*)(attributes[i].getName()));        
                    }
                }
            } // End iterate over attributes
            
            LOGEXIT(INFO,"SDOSAX2Parser:setAttributes");

        }


        const PropertyImpl* SDOSAX2Parser::handleOpenType(
                                    SDOXMLString& tns,
                                    const SDOXMLString& localname,
                                    DataObjectImpl* dob,
                                    const SAX2Namespaces& namespaces,
                                    const SAX2Attributes& attributes,
                                    SDOXMLString& xsitypeURI,
                                    SDOXMLString& xsitypeName,
                                    bool bToBeNull)
        {
            // first, see if there is a global element or attribute corresponding...
            const PropertyImpl* pprop;
            DataObjectPtr newDO = 0;
            try 
            {
                const TypeImpl* ti = 0;
                const PropertyImpl* prop = 0;
                SDOXMLString propertyName;

                ti = dataFactory->findTypeImpl(tns,"RootType");
                if (ti != 0)
                {
                    propertyName = getSDOName((Type&)*ti, localname);
                    prop = ti->getPropertyImpl(propertyName);
                }
                else
                {
                    propertyName = localname;
                }

                if (prop != 0)
                {
                    if (prop->isMany())
                    {
                        pprop = ((DataObjectImpl*)dob)->defineList(propertyName);

                        // the type of the list needs to be set, as chars sets a CString
                        try 
                        {
                            DataObjectList& dl = ((DataObjectImpl*)dob)->getList((const char*)propertyName);
                            ((DataObjectListImpl*)&dl)->setType(prop->getType().getURI(),
                                prop->getType().getName());
                        }
                        catch (SDORuntimeException)
                        {
                            // let it pass - the type will be Bytes
                        }

                        if (prop->getType().isDataType())
                        {
                            currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                bToBeNull);
                        }
                        else
                        {
                            newDO = dataFactory->create(
                                prop->getType().getURI(),
                                prop->getType().getName());
                            // here we need to use the sequence interface if it exists.
                            if (dob->getType().isSequencedType())
                            {
                                SequencePtr seq = currentPropertySetting.dataObject->getSequence();
                                seq->addDataObject(propertyName,newDO);
                            }
                            else
                            {
                                DataObjectList& dol = dob->getList((const char*)propertyName);
                                dol.append(newDO);
                            }
                            setCurrentDataObject(newDO);
                            setAttributes(tns, namespaces,attributes);
                        }
                        return pprop;
                    }
                    else
                    {
                        switch (prop->getTypeEnum())
                        {
                            case Type::BooleanType:
                                pprop = ((DataObjectImpl*)dob)->defineBoolean((const char*)propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::ByteType:
                                pprop = ((DataObjectImpl*)dob)->defineByte((const char*)propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::CharacterType:
                                pprop = ((DataObjectImpl*)dob)->defineCharacter(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::BytesType:
                                pprop = ((DataObjectImpl*)dob)->defineBytes(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::StringType:
                                pprop = ((DataObjectImpl*)dob)->defineString(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::ShortType:
                                pprop = ((DataObjectImpl*)dob)->defineShort(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::IntType:
                                pprop = ((DataObjectImpl*)dob)->defineInt(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::LongType:
                                pprop = ((DataObjectImpl*)dob)->defineLong(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::DoubleType:
                                pprop = ((DataObjectImpl*)dob)->defineDouble(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::FloatType:
                                pprop = ((DataObjectImpl*)dob)->defineFloat(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::DateType:
                                pprop = ((DataObjectImpl*)dob)->defineDate(propertyName);
                                currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                    bToBeNull);
                                break;
                            case Type::DataObjectType:
                                pprop = ((DataObjectImpl*)dob)->defineDataObject(propertyName,
                                prop->getType().getURI(), prop->getType().getName());
                                newDO = dataFactory->create(
                                    prop->getType().getURI(),
                                    prop->getType().getName());
                                // here we need to use the sequence interface if it exists.
                                if (dob->getType().isSequencedType())
                                {
                                    SequencePtr seq = dob->getSequence();
                                    seq->addDataObject(propertyName, newDO);
                                }
                                else
                                {
                                    dob->setDataObject((const char*)propertyName, newDO);
                                }
                                setCurrentDataObject(newDO);
                                setAttributes(tns,namespaces,attributes);
                                break;
                        }
                    } // else
                } // if prop != 0
                else 
                {
                    // The type is open, and the property doesnt exist, so we are creating
                    // a property, and need to find out the type to create.
                    // As I cannot tell if its a single value or many valued, I create all
                    // as many valued
                    // could be data object or primitive. All primitives will appear
                    // as bytes.
                    // UPDATE: Spec says that all elements will appear as DataObjects which 
                    // are sequenced - the text will come out as text elements in the sequence

                    if (!xsitypeName.isNull())
                    {
                        // it has a type from xsi:type
                        newDO = dataFactory->create((const char*)xsitypeURI, (const char*)xsitypeName);
                    }
                    else
                    {
                        newDO = dataFactory->create(SDOUtils::sdoURI, "OpenDataObject");
                    }
                    pprop = ((DataObjectImpl*)dob)->defineList(propertyName);
                   // here we need to use the sequence interface if it exists.
                    if (dob->getType().isSequencedType())
                    {
                        SequencePtr seq = dob->getSequence();
                        seq->addDataObject(propertyName, newDO);
                    }
                    else
                    {
                        DataObjectList& dol = dob->getList((const char*)propertyName);
                        dol.append(newDO);
                    }
                    setCurrentDataObject(newDO);
                    setAttributes(tns,namespaces,attributes);
                }
                return pprop;
            }
            catch (SDORuntimeException)
            {
                // fail to find the property or create a dummy
                return 0;
            }
        }

        void SDOSAX2Parser::startElementNs(
            const SDOXMLString& localname,
            const SDOXMLString& prefix,
            const SDOXMLString& URI,
            const SAX2Namespaces& namespaces,
            const SAX2Attributes& attributes)
            
        {
            LOGENTRY(INFO,"SDOSAX2Parser: startElementNs");

            LOGINFO_1(INFO,"SDOSAX2Parser: startElementNs:%s",
                (const char*)localname);

            newSequence = true;

            bool bToBeNull = false;
            // Save the namespace information from the first element
            if (setNamespaces)
            {
                documentNamespaces = namespaces;
                setNamespaces = false;
            }
            else
            {
                documentNamespaces.merge(namespaces);
            }
            
            if (ignoreEvents)
            {
                // Check for the tag we are waiting for
                if (   (ignoreTag.localname.equals(localname))
                    && (ignoreTag.uri.equals(URI))
                    && (ignoreTag.prefix.equals(prefix)) )
                {
                    ignoreTag.tagCount++;
                }
                LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit1");
                return;
            }

            if (URI.equalsIgnoreCase("http://www.w3.org/2001/XMLSchema"))
            {
                // ignore anything within a schema
                LOGINFO_1(INFO,"SDOSAX2Parser ignores schema element:%s",
                                    (const char *)localname);

                // We need to ignore all events until the end tag for this element
                ignoreEvents = true;
                ignoreTag.localname = localname;
                ignoreTag.uri = URI;
                ignoreTag.prefix = prefix;
                ignoreTag.tagCount = 0;
                return;
            }

 
            
            if (dealingWithChangeSummary)
            {
                if (csbuilder == 0)
                {
                    LOGERROR(ERROR,"SDOSAX2Parser:Parser builds summary with no builder");
                    LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit2");
                    return;
                }
                csbuilder->processStart(
                    localname,
                    prefix,
                    URI,
                    namespaces,
                    attributes);
                LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit3");
                return;
            }


            if (URI.equalsIgnoreCase(SDOUtils::sdoURI))
            {
                ///////////////////////////////////////////////////////////////////////
                // Handle datagraph
                ///////////////////////////////////////////////////////////////////////
                if (localname.equalsIgnoreCase("datagraph"))
                {
                    // Remember this is a datagraph. The root DO will be created
                    // later when we can have a better guess at the namespaceURI
                    isDataGraph = true;
                } // end handling sdo:datagraph
                
                ////////////////////////////////////
                // Handle ChangeSummary on datagraph
                ////////////////////////////////////
                if (localname.equals("changeSummary"))
                {
                    changeSummary = true;
                    changeSummaryDO = currentDataObject;

                    csbuilder = new ChangeSummaryBuilder(
                        dataFactory, rootDataObject );

                    changeSummaryLogging = true;

                    
                    SDOXMLString logging = attributes.getValue("logging");
                    if (!logging.isNull())
                    {
                        if (logging.equals("false"))
                        {
                            changeSummaryLogging = false;
                        }
                    }

                    LOGINFO(INFO,"SDOSAX2Parser:Start change summary");
                    dealingWithChangeSummary = true;
                    LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit4");
                    return;
                    
                }
                
            }
            else
            {
                ///////////////////////////////////////////////////////////////////////
                // Each element is a DataObject or a Property on the current DO
                ///////////////////////////////////////////////////////////////////////
                DataObjectPtr newDO = 0;
                
                SDOXMLString typeURI, typeName, propertyName;
                
                ///////////////////////////////////////////////////////////////////////
                // Determine the type. It is either specified by the xsi:type attribute
                // or the localname is the name of a property on "RootType"
                ///////////////////////////////////////////////////////////////////////
                int i;
                for (i=0; i < attributes.size(); i++)
                {
                    if (attributes[i].getUri().equalsIgnoreCase("http://www.w3.org/2001/XMLSchema-instance"))
                    {
                        if (attributes[i].getName().equalsIgnoreCase("type"))
                        {
                            SDOXMLString fullTypeName = attributes[i].getValue();
                            SDOXMLString pref;

                            int index = fullTypeName.firstIndexOf(':');
                            if (index < 0)
                            {
                                typeName = fullTypeName;
                            }
                            else
                            {    
                                // Is the namespace prefix defined?
                                typeName = fullTypeName.substring(index+1);
                                pref = fullTypeName.substring(0, index);
                            }

                            // Convert the prefix to a namespace URI
                            const SDOXMLString* namespaceURI = namespaces.find(pref);
                            if (namespaceURI == 0)
                            {
                                namespaceURI = documentNamespaces.find(pref);
                            }
                            if (namespaceURI != 0)
                            {
                                typeURI = *namespaceURI;
                            }
                        }
                        else if (attributes[i].getName().equalsIgnoreCase("nil"))
                        {
                            if (attributes[i].getValue().equalsIgnoreCase("true"))
                            {
                                // the current setting needs to be setNull
                                bToBeNull = true;
                            }
                        }
                    }
                } // End - attribute loop
                
                if (typeURI.isNull())
                {
                    typeURI = "";
                }
                
                SDOXMLString tns = URI;
                
                if (tns.isNull())
                    tns = "";
                
                try
                {
                    if (currentDataObject == 0)
                    {
                        // This element should become the root data object
                        
                        // Target namespace will be:
                        //   the targetNamespaceURI if specified 
                        //   or the URI of xsi:type if specified
                        //   or the URI of this element
                        if (!typeURI.equals(""))
                        {
                            tns = typeURI;
                        }

                        if (!targetNamespaceURI.isNull() && !targetNamespaceURI.equals(""))
                        {
                            tns = targetNamespaceURI;
                            rootElementURI = tns;
                        }
                        else
                        {
                            rootElementURI = URI;
                        }
                        
                        // Check for localname as a property of the RootType
                        // if we do not already know the type
                        if (typeName.isNull())
                        {
                            try {
                                const Type& rootType = dataFactory->getType(tns, "RootType");
                                propertyName = getSDOName(rootType, localname);
                                const Type& newType = 
                                    ((TypeImpl&)(rootType)).getRealPropertyType(propertyName);

                                typeURI = newType.getURI();
                                typeName = newType.getName();
                            }
                            catch (const SDOTypeNotFoundException&)
                            {
                                typeURI = SDOUtils::sdoURI;
                                typeName = "OpenDataObject";
                            }
                        }
                        
                        // Create the root DataObject
                        if (isDataGraph)
                        {
                            DataObjectPtr rootdo = dataFactory->create(tns, "RootType");
                            setCurrentDataObject(rootdo);
                            changeSummaryDO = currentDataObject;
                        }
                        else
                        {
                            rootElementName = localname;
                        }
                        
                        // NOTE: always creating DO doesn't cater for DataType as top element

                        const Type& tp = dataFactory->getType((const char*)typeURI,typeName);
                        if (tp.isDataType())
                        {
                            newDO = dataFactory->create(tns, "RootType");
                            currentPropertySetting = PropertySetting(newDO, localname,
                            bToBeNull);

                            // TODO - need instead to record the fact that its a primitive, not
                            // a real DO - and then present it as the root.
                           // newDO = dataFactory->create(tns, "RootType");
                           // const Type& tpr = dataFactory->getType(tns,"RootType");
                           // XSDTypeInfo* typeInfo = (XSDTypeInfo*)
                           //   ((DASType*)&tpr)->getDASValue("XMLDAS::TypeInfo");
                           // if (typeInfo)
                           // {
                           //     TypeDefinitionImpl* td;
                           //     td = (TypeDefinitionImpl*)&(typeInfo->getTypeDefinition());
                           //     if (td)td->isExtendedPrimitive = true;
                           //     currentPropertySetting = PropertySetting(newDO, "value" /*localname*/,
                           //     bToBeNull);

                           // }
                           // else
                           // {
                           //     currentPropertySetting = PropertySetting(newDO, localname,
                           //     bToBeNull);
                           // }

                        }
                        else
                        {

                            newDO = dataFactory->create((const char*)typeURI, (const char*)typeName);

                            // get the type definition, and see if its an extended primitive.

                            XSDTypeInfo* typeInfo = (XSDTypeInfo*)
                              ((DASType*)&tp)->getDASValue("XMLDAS::TypeInfo");
                            if (typeInfo)
                            {
                                const TypeDefinitionImpl& typeDefinition = typeInfo->getTypeDefinition();
                                if (typeDefinition.isExtendedPrimitive)
                                {
                                    // The name of this element is the name of a property on the current DO
                                    currentPropertySetting = PropertySetting(newDO, localname,
                                    bToBeNull);
                                }
                            }
                        }
                        
                    } // End - currentDataObject == 0
                    
                    else
                    { // currentDataObject != 0
                        
                        // Get the Property from the dataObject
                        propertyName = getSDOName(*currentDataObjectType, localname);
                        const Type& type = currentDataObject->getType();

                    
                        // go lower level so we can find open properties w/o exception
                        DataObject* dob = getRawPointer(currentDataObject);
                        const PropertyImpl* pprop = ((DataObjectImpl*)dob)->getPropertyImpl(propertyName);
                        if (pprop == 0)
                        {
                            if (type.isOpenType())
                            {
                                pprop = handleOpenType(
                                    tns,
                                    localname,
                                    (DataObjectImpl*)dob,
                                     namespaces,
                                     attributes,
                                     typeURI,
                                     typeName,
                                     bToBeNull);
                            }
                            if (pprop == 0)
                            {
                                // this is an open property , we will need to create it
                                LOGERROR_1(WARNING,"SDOSAX2Parser Unknown element:%s",
                                    (const char *)localname);

                                // We need to ignore all events until the end tag for this element
                                ignoreEvents = true;
                                ignoreTag.localname = localname;
                                ignoreTag.uri = URI;
                                ignoreTag.prefix = prefix;
                                ignoreTag.tagCount = 0;
                                if (setter != 0)
                                {
                                    char *msg = new char[strlen((const char*)localname) + 32];
                                    if (msg) {
                                        sprintf(msg,"Parser found unknown element %s",
                                            (const char*)localname);
                                        setter->setError( msg );
                                        delete[] msg;
                                    }
                                }
                             }
                            LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit5");
                            return;
                        }
                        else
                        {

                            const Property& prop = (Property&)*pprop;
                            const TypeImpl* propType = ((TypeImpl&)type).getRealPropertyTypeImpl(propertyName);
                            if (propType == 0)
                            {
                                // could be a previously created open type property
                                propType = (const TypeImpl*)pprop->getTypeImpl();
                            }
                            if (propType != 0)
                            {
                                XSDPropertyInfo* pi = (XSDPropertyInfo*)((DASProperty*)&prop)->getDASValue("XMLDAS::PropertyInfo");
                                if ((pi && pi->getPropertyDefinition().isIDREF)
                                || prop.isReference())
                                {
                                    // The name of this element is the name of a property on the current DO
                                    currentPropertySetting = PropertySetting(currentDataObject, propertyName, bToBeNull,
                                        true);                        
                                }
                        
                                // If it is a DataType then we need set the value
                                else if (propType->isDataType() )
                                {
                                    // The name of this element is the name of a property on the current DO
                                    currentPropertySetting = PropertySetting(currentDataObject, propertyName,
                                        bToBeNull);
                                }
                                else
                                {

                                    // If typeName is not set then create object of Type of Property
                                    // otherwise use the typeURI and typeName specified by e.g. xsi:type
                                    if (typeName.isNull())
                                    {
                                        newDO = dataFactory->create(propType->getURI(), propType->getName());
                                    }
                                    else
                                    {
                                        newDO = dataFactory->create((const char*)typeURI, (const char*)typeName);
                                    }

                                    XSDTypeInfo* typeInfo = (XSDTypeInfo*)
                                    ((DASType*)propType)->getDASValue("XMLDAS::TypeInfo");
                                    if (typeInfo && typeInfo->getTypeDefinition().isExtendedPrimitive)
                                    {
                                        // The name of this element is the name of a property on the current DO
                                        currentPropertySetting = PropertySetting(newDO, "value", bToBeNull);
                                    }

                                }
                            }
                        }  // End // currentDataObject != 0
                    } // end prop != 0
                    if (newDO)
                    {
                        if (!setDO(newDO, propertyName))
                        {
                            LOGERROR_1(WARNING,"SDOSAX2Parser Unknown element:%s",
                                (const char *)localname);

                            // We need to ignore all events until the end tag for this element
                            ignoreEvents = true;
                            ignoreTag.localname = localname;
                            ignoreTag.uri = URI;
                            ignoreTag.prefix = prefix;
                            ignoreTag.tagCount = 0;
                            if (setter != 0)
                            {
                                char *msg = new char[strlen((const char*)localname) + 32];
                                if (msg) {
                                    sprintf(msg,"Parser found unknown element %s",
                                    (const char*)localname);
                                    setter->setError( msg );
                                    delete msg;
                                }
                            }
                            LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit6");
                            return;
                        }
                    }
                } // end try

                catch (const SDOTypeNotFoundException& )
                {
                    
                    LOGERROR_1(WARNING,"SDOSAX2Parser Unknown element:%s",
                        (const char *)localname);

                    // We need to ignore all events until the end tag for this element
                    ignoreEvents = true;
                    ignoreTag.localname = localname;
                    ignoreTag.uri = URI;
                    ignoreTag.prefix = prefix;
                    ignoreTag.tagCount = 0;
                    if (setter != 0)
                    {
                        char *msg = new char[strlen((const char*)localname) + 32];
                        if (msg) {
                            sprintf(msg,"Parser found unknown element %s",
                            (const char*)localname);
                            setter->setError( msg );
                            delete[] msg;
                        }
                    }
                    LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit7");
                    return;
                }

#ifdef _DEBUG
                catch (const SDOPropertyNotFoundException& e )
#else
                catch (const SDOPropertyNotFoundException&  )
#endif
                {
                    LOGERROR_1(WARNING,"SDOSAX2Parser Unknown element exception:%s",
                        (const char *)localname);
                    LOGSDOEXCEPTION(WARNING,"Exception:",e);

                    // We need to ignore all events until the end tag for this element
                    ignoreEvents = true;
                    ignoreTag.localname = localname;
                    ignoreTag.uri = URI;
                    ignoreTag.prefix = prefix;
                    ignoreTag.tagCount = 0;
                    if (setter != 0)
                    {
                        char *msg = new char[strlen((const char*)localname) + 32];
                        if (msg)
                        {
                            sprintf(msg,"Parser found unknown element %s",
                                 (const char*)localname);
                            setter->setError( msg );
                            delete msg;
                        }
                    }
                    LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit8");
                    return;
                }

                setAttributes(tns,namespaces, attributes);
                
            }
            LOGEXIT(INFO,"SDOSAX2Parser: startElementNs - exit9");
        }
        
        
        void SDOSAX2Parser::endElementNs(
            const SDOXMLString& localname,
            const SDOXMLString& prefix,
            const SDOXMLString& URI)
        {

            LOGENTRY(INFO,"SDOSAX2Parser: endElementNs");

            newSequence = true;

            if (localname.equals("changeSummary"))
            {
                // end of change summary
                dealingWithChangeSummary = false;
                LOGINFO(INFO,"SDOSAX2Parser: Finished change summary");
                LOGEXIT(INFO,"SDOSAX2Parser: endElementNs - exit1");
                return;
            }

            if (dealingWithChangeSummary)

            {
                if (csbuilder == 0) 
                {
                    LOGERROR(WARNING,"SDOSAX2Parser: End change summary with no builder");
                    LOGEXIT(INFO,"SDOSAX2Parser: endElementNs - exit2");
                    return;
                }
                csbuilder->processEnd(localname,
                                     prefix,
                                     URI);
                LOGEXIT(INFO,"SDOSAX2Parser: endElementNs - exit3");
                return;
            }


            if (ignoreEvents)
            {
                // Check for the tag we are waiting for
                if (   (ignoreTag.localname.equals(localname))
                    && (ignoreTag.uri.equals(URI))
                    && (ignoreTag.prefix.equals(prefix)) )
                {
                    if (ignoreTag.tagCount == 0)
                    {
                        ignoreEvents = false;
                    }
                    ignoreTag.tagCount--;
                }
                LOGEXIT(INFO,"SDOSAX2Parser: endElementNs - exit4");
                return;
            }
            
            // If currentPropertySetting is set (name is not null)
            // then we need to set the property now
            if (!currentPropertySetting.name.isNull())
            {
                if (currentPropertySetting.isNULL)
                {

                    currentPropertySetting.dataObject->
                            setNull((const char*)currentPropertySetting.name);

                }
                else 
                {    
                    if (currentPropertySetting.value.isNull())
                    {
                        currentPropertySetting.value = SDOXMLString("");
                    }
                    try
                    {
                        const Type& tp = currentPropertySetting.dataObject->getType();
                        XSDTypeInfo* typeInfo = (XSDTypeInfo*)
                            ((DASType*)&tp)->getDASValue("XMLDAS::TypeInfo");
                        if (typeInfo && typeInfo->getTypeDefinition().isExtendedPrimitive)
                        {
                            PropertyPtr p = currentPropertySetting.dataObject->getInstanceProperty(
                                "value"); 
                            if (!p) {
                              throw SDOPropertyNotFoundException(TUSCANY_SDO_EINFO,
                                                                 "Cannot find property: value");
                            }
                            if (p->isMany())
                            {
                                // use the sequence interface if it exists.
                                if (currentPropertySetting.dataObject->getType().isSequencedType())
                                {
                                    SequencePtr seq = currentPropertySetting.dataObject->getSequence();
                                    seq->addCString("value", currentPropertySetting.getStringWithCDataMarkers().c_str());
                                }
                                else
                                {
                                    DataObjectList& dl = currentPropertySetting.dataObject->
                                    getList((const char*)"value");
                                    dl.append((const char*)currentPropertySetting.getStringWithCDataMarkers().c_str());
                                }

                            }
                            else
                            {
                                // use the sequence interface if it exists
                                if (currentPropertySetting.dataObject->getType().isSequencedType())
                                {
                                    SequencePtr seq = currentPropertySetting.dataObject->getSequence();
                                    seq->addCString("value", currentPropertySetting.getStringWithCDataMarkers().c_str());
                                }
                                else
                                {
                                    currentPropertySetting.dataObject->
//                                    setCString((const char*)"value", currentPropertySetting.value );
                                    setCString((const char*)"value", currentPropertySetting.getStringWithCDataMarkers().c_str() );
                                }
                            }
                            if (dataObjectStack.size() == 0 || rootDataObject == dataObjectStack.top())
                            {
                                currentDataObject = 0;
                                currentDataObjectType = 0;
                            }
                            else
                            {
                                dataObjectStack.pop();
                                currentDataObject = dataObjectStack.top();
                                currentDataObjectType = &(currentDataObject->getType());
                            }

                        }
                        else
                        {
                            if (currentPropertySetting.isIDREF)
                            {
                                // remember this value to resolve later
                                IDRef ref(currentPropertySetting.dataObject,
                                    currentPropertySetting.name,
                                    currentPropertySetting.value );
                                IDRefs.push_back(ref);
                            }
                            else
                            {
                                if (currentPropertySetting.dataObject->getType().isSequencedType())
                                {
                                    SequencePtr seq = currentPropertySetting.dataObject->getSequence();
                                    seq->addCString(currentPropertySetting.name, currentPropertySetting.getStringWithCDataMarkers().c_str());
                                }
                                // Always set the property as a String. SDO will do the conversion

                                // It might be a single setting for a many-valued property.
                                // may throw SDOPropertyNotFoundException
                                else {
                                    PropertyPtr p = currentPropertySetting.dataObject->getInstanceProperty(
                                        (const char*)currentPropertySetting.name);
                                    if (!p) {
                                      SDOString msg("Cannot find property: ");
                                      msg += (const char*)currentPropertySetting.name;
                                      throw SDOPropertyNotFoundException(TUSCANY_SDO_EINFO,
                                                                         msg.c_str());
                                    }
                                    if (p->isMany())
                                    {
                                        DataObjectList& dl = currentPropertySetting.dataObject->
                                        getList(*p);
                                        dl.append((const char*)currentPropertySetting.getStringWithCDataMarkers().c_str());
                                    }
                                    else
                                    {
                                        currentPropertySetting.dataObject->
                                        setCString(*p, currentPropertySetting.getStringWithCDataMarkers().c_str() );
                                    }
                                }
                            }
                        }
                    }
#ifdef _DEBUG
                    catch (const SDOPropertyNotFoundException& e)
#else
                    catch (const SDOPropertyNotFoundException&)
#endif
                    {
                        LOGSDOEXCEPTION(WARNING,"SDOSAX2Parser error attribute (ignored)",e);         
                    }
                }
                currentPropertySetting = PropertySetting();
                
            }
            else
            {
                if (changeSummary 
                    && changeSummaryLogging 
                    && changeSummaryDO == currentDataObject)
                {
                    // Set logging on for this DO before it is popped from stack
                    ChangeSummaryPtr cs = currentDataObject->getChangeSummary();
                    if (cs)
                    {
                        cs->beginLogging();
                    }
                    changeSummary = false;
                }
                
                if (dataObjectStack.size() == 0 || rootDataObject == dataObjectStack.top())
                {
                    currentDataObject = 0;
                    currentDataObjectType = 0;
                }
                else
                {
                    dataObjectStack.pop();
                    currentDataObject = dataObjectStack.top();
                    currentDataObjectType = &(currentDataObject->getType());
                }
            }
            LOGEXIT(INFO,"SDOSAX2Parser: endElementNs - exit4");
        }


         
        void SDOSAX2Parser::characters(const SDOXMLString& chars)
        {
            if (chars.isNull()) return;

            if (!strcmp((const char*)chars,"\r") ||
                !strcmp((const char*)chars,"\n"))
            {
                newSequence = true;
                return;
            }

            if (dealingWithChangeSummary)
            {
                if (csbuilder == 0)
                {
                    LOGERROR(WARNING,"SDOSAX2Parser: no builder");
                    return;
                }
                csbuilder->processChars(chars);
                return;
            }

            if (ignoreEvents)
                return;

            if (!currentPropertySetting.name.isNull())
            {
                currentPropertySetting.value = currentPropertySetting.value + chars;    
                return;
            }
            DataObject* dob = getRawPointer(currentDataObject);
            if ((dob != 0)  && ((DataObjectImpl*)dob)->getTypeImpl().isFromList())
            {
                 // this is a list,so we need to split it up
                DataObjectList& dl = currentDataObject->getList(
                       (const char *)"values");

                const char* str = (const char*)chars;
         
                // Convert any synthetic CDATA markers back to the real thing
                SDOString valueString(str);
                SDOString tmpString = SDOUtils::replace(valueString, SDOUtils::CDataStartMarker, SDOUtils::XMLCDataStartMarker);
                tmpString = SDOUtils::replace(tmpString, SDOUtils::CDataEndMarker, SDOUtils::XMLCDataEndMarker);  
                str =  (const char*)tmpString.c_str();  
                    
                char* buf = new char[strlen(str)+1];
                if (!buf) return;

                strcpy(buf,str);

                int start_point = 0;
                int end_point;
                int final = strlen(buf);

                do {
                   if (start_point >= final)break;
                   while (buf[start_point] == (char)0x20 || buf[start_point] == (char)0x09
                       || buf[start_point] == (char)0x0A || buf[start_point] == (char)0x0D )start_point++;
                   end_point = start_point;
                   while (buf[end_point] != (char)0x20 && buf[end_point] != (char)0x09 &&
                          buf[end_point] != (char)0x0A && buf[end_point] != (char)0x0D &&
                                                                        buf[end_point] != 0x0)end_point++;
                   if (end_point == start_point)break; 
                   *(buf+end_point) = 0;
                   dl.append((const char*)(buf+start_point));
                   start_point = end_point + 1;
                } while(1);

                delete[] buf;
                return;
            }


            // If the current DataObject is a sequenced Type
            // then add this as text to the sequence
            if (currentDataObject && currentDataObjectType->isSequencedType())
            {
                // Convert any synthetic CDATA markers back to the real thing     
                const char* str = (const char*)chars;
            
                SDOString valueString(str);
                SDOString tmpString = SDOUtils::replace(valueString, SDOUtils::CDataStartMarker, SDOUtils::XMLCDataStartMarker);
                tmpString = SDOUtils::replace(tmpString, SDOUtils::CDataEndMarker, SDOUtils::XMLCDataEndMarker);  
                str =  tmpString.c_str(); 
                                
                SequencePtr seq = currentDataObject->getSequence();
                if (seq)
                {
                    if (newSequence == true)
                    {
                        seq->addText(str);
                        newSequence = false;
                    }
                    else
                    {
                        for (int k= (int)(seq->size())-1; k>=0 ; k --)
                        {
                            if (seq->isText(k))
                            {
                                const char * s = seq->getCStringValue(k);
                               
                                if (s)
                                {
                                    char *combi = 
                                        new char[strlen(s)+strlen(str) + 2];
                                    strcpy(combi,s);
                                    strcat(combi,str);
                                    seq->setText(k,(const char*)combi);
                                    delete[] combi;
                                }
                                else
                                {
                                    seq->setText(k,str);
                                }
                                return;
                            }
                        }
                        seq->addText(str);
                    }
                    return;
                }
            }
           
        }
        
        
        void SDOSAX2Parser::setCurrentDataObject(DataObjectPtr currentDO)
        {    
            currentDataObject = currentDO;
            dataObjectStack.push(currentDataObject);
            currentDataObjectType = &(currentDataObject->getType());
            if (rootDataObject == 0)
            {
                rootDataObject = currentDataObject;
            }
        }

        const SDOXMLString& SDOSAX2Parser::getSDOName(const Type& type, const SDOXMLString& localName)
        {

/*            XSDTypeInfo* typeInfo = (XSDTypeInfo*)((DASType*)&type)->getDASValue("XMLDAS::TypeInfo");
            if (typeInfo)
            {
                const TypeDefinitionImpl& typeDefinition = typeInfo->getTypeDefinition();
                XmlDasPropertyDefs::const_iterator propsIter;
                for (propsIter = typeDefinition.properties.begin(); propsIter != typeDefinition.properties.end(); propsIter++)
                {
                    const PropertyDefinitionImpl& prop = *propsIter;
                    if (prop.localname.equals(localName))
                    {
                        return prop.name;
                    }
                    for (int i=0;i< prop.substituteNames.size();i++)
                    {
                        if (prop.substituteLocalNames[i].equals(localName))
                        {
                            return prop.substituteNames[i];
                            // possibly should be return prop.name;
                        }
                    }
                }                    
            }
            */

            const TypeImpl& typeImpl = dynamic_cast<const TypeImpl&>(type);
            const std::vector<PropertyImplPtr> pl = typeImpl.getPropertyListReference();

            for (std::vector<PropertyImplPtr>::const_iterator i = pl.begin();
                 i != pl.end();
                 i++)
            {
                XSDPropertyInfo* pi = (XSDPropertyInfo*)
                ((DASProperty*) getRawPointer(*i))->getDASValue("XMLDAS::PropertyInfo");

                if (pi)
                {
                    const PropertyDefinitionImpl&  propdef = pi->getPropertyDefinition();
                    if (localName .equals(propdef.localname))
                        return propdef.name;

                    for (unsigned int j=0;j< propdef.substituteNames.size();j++)
                    {
                        if (propdef.substituteLocalNames[j].equals(localName))
                        {
                            return propdef.substituteNames[j];
                            // possibly should be return propdef.name;
                        }
                    }
                }
            }
                


            return localName;
        }


        std::istream& operator>>(std::istream& input, SDOSAX2Parser& parser)
        {
            parser.stream(input);
            
            return input;
        }
        
        std::istringstream& operator>>(std::istringstream& input, SDOSAX2Parser& parser)
        {
            parser.stream(input);
            
            return input;
        }
        
    } // End - namespace sdo
} // End - namespace commonj

