/*
 * 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/ChangeSummaryBuilder.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 <stdio.h>
#include <stdlib.h>
namespace commonj
{
    namespace sdo
    {
        
// ========================================================================
// Constructor/Destructor
// ========================================================================

        ChangeSummaryBuilder::ChangeSummaryBuilder(
            DataFactoryPtr df,
            DataObjectPtr& rootDO)
            
            : dataFactory(df),
            rootDataObject(rootDO)
            
    
        {
            LOGINFO(INFO,"CSBuilder constructor");
            currentState = baseState;
        }
        
        ChangeSummaryBuilder::~ChangeSummaryBuilder()
        {
            LOGINFO(INFO,"CSBuilder destructor");
        }
        
        
// ========================================================================
// Deletion handling
// ========================================================================

        void ChangeSummaryBuilder::populateDeletion(ChangeSummaryImplPtr csi,DataObjectPtr dob,
                                            int currentIndex)
        {
            LOGENTRY(INFO,"CSBuilder populateDeletion");
            try {
                
                std::list<deletionAttribute>::iterator  attributesiter;
                std::list<deletionElement>::iterator    elementsiter;

                for (attributesiter=  deletions[currentIndex].attributes.begin();
                     attributesiter!= deletions[currentIndex].attributes.end();
                     ++attributesiter)
                {

                    LOGINFO_1(INFO,"CSBuilder Populate deletion attribute:%s",
                    (const char*)((*attributesiter).name));

                    dob->setCString((const char*)((*attributesiter).name),
                                    (const char*)((*attributesiter).value));
                }
                for (elementsiter=  deletions[currentIndex].elements.begin();
                     elementsiter!= deletions[currentIndex].elements.end();
                     ++elementsiter)
                {

                    LOGINFO_1(INFO,"CSBuilder:Populate deletion element:%s",
                    (const char*)((*elementsiter).name));
                    
                    SDOXMLString value = (*elementsiter).value;
                    SDOXMLString prop = (*elementsiter).name;

                    if ((*elementsiter).isDeletion)
                    {
                        for (unsigned int i = 0; i < deletions.size(); i++)
                        {
                            if (value.equals (deletions[i].reference))
                            {
                                if (deletions[i].dob == 0)
                                {
                                    handleDeletion(csi,i,dob,(*elementsiter).value,
                                        (*elementsiter).name);
                                }
                                // dob may have been set by the line above.
                                if (deletions[i].dob != 0)
                                {
                                    DataObjectList& dli = dob->getList((const char*)prop);
                                    dli.insert(0,deletions[i].dob);
                                }
                                break;
                            }
                        }
                    }
                    else
                    {
                        DataObjectList& dli = dob->getList((const char*)prop);
                        dli.append((const char*)value);
                    }
                }
            }
            catch (SDORuntimeException e)
            {
                LOGSDOEXCEPTION(ERROR,"Error populating a deletion record",e);
            }
            LOGEXIT(INFO,"CSBuilder populateDeletion");
            return;
        }


// ========================================================================
// Deletion handling
// ========================================================================

        void ChangeSummaryBuilder::handleDeletion(
                              ChangeSummaryImplPtr csi, 
                              int currentIndex, 
                              DataObjectPtr cont,
                              SDOXMLString path,
                              SDOXMLString prop)
        {
            LOGENTRY(INFO,"CSBuilder handleDeletion(2)");

            if (deletions[currentIndex].completedprocessing)
            {
                LOGEXIT(INFO,"CSBuilder handleDeletion(2) - Exit1");
                return;
            }

            PropertyPtr p = cont->getInstanceProperty((const char*)prop);
            if (!p) {
              SDOString msg("Cannot find property: ");
              msg += (const char*)prop;
              throw SDOPropertyNotFoundException(TUSCANY_SDO_EINFO,
                                                 msg.c_str());
            }
            DataObjectPtr dob = dataFactory->create(
                                p->getType());
            populateDeletion(csi,dob,currentIndex);
            deletions[currentIndex].dob = dob;
            deletions[currentIndex].completedprocessing=true;
            csi->appendToDeletions(*p,dob,cont,path);

            LOGEXIT(INFO,"CSBuilder handleDeletion(2) - Exit2");

            return;
        }

// ========================================================================
// Deletion handling
// ========================================================================

        void ChangeSummaryBuilder::handleDeletion(
                              ChangeSummaryImplPtr csi, 
                              int currentIndex, 
                              SDOXMLString path)
        {

            // find the original container - and see if it
            // still exists.
            // supposing the container doesnt exist, we need
            // to process the deletion of that one first, or
            // locate it in the already deleted elements.

            LOGENTRY(INFO,"CSBuilder:handleDeletion");

            if (deletions[currentIndex].completedprocessing)
            {
                LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit1");
                return;
            }

            int index = path.lastIndexOf('/');
            if (index < 0)
            {
                LOGERROR(WARNING,"CSBuilder logs deletion of the root object - not possible");
                LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit2");
                return;
            }
            
            SDOXMLString prop = 
                    path.substring(index+1);
            SDOXMLString contpath =
                    path.substring(0,index);

            index = prop.lastIndexOf('.');
            if (index >= 0) prop = prop.substring(0,index);

            index = prop.lastIndexOf('[');
            if (index >= 0) prop = prop.substring(0,index);

            DataObjectPtr cont;

            if (contpath.isNull())
            {
                cont = rootDataObject;

                LOGINFO_1(INFO,"Handling deletion from root of %s",(const char*)prop);
            }
            else 
            {
                try {
                    cont = rootDataObject->getDataObject((const char*)contpath);
                    LOGINFO_2(INFO,"Handling deletion from %s of %s",
                        (const char*)contpath,(const char*)prop);
                }
                catch (SDORuntimeException e)
                {
                    cont  = 0;
                    LOGINFO(INFO,"Container was a deleted object");
                }
            }

            if (cont != 0)
            {
                handleDeletion(csi,currentIndex,cont,path,prop);
                LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit3");
                return;
            }
            else
            {
                // this data object was also deleted
                cont = csi->matchDeletedObject(contpath);
                if (cont != 0)
                {
                    // we found a previously deleted object for 
                    // the container.
                    handleDeletion(csi,currentIndex,cont,path,prop);
                    LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit4");
                    return;
                }

            }

            // it could be a deletion which has not been processed yet
            for (unsigned int i = 0; i < deletions.size(); i++)
            {
                if (contpath.equals (deletions[i].reference))
                {
                    // matching deletion found
                    if (i == currentIndex)
                    {
                        LOGERROR(WARNING,"Loop detected in handling deletes");
                        LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit5");
                        return;
                    }
                    // Handle the deletion first
                    handleDeletion(csi,i,contpath);
                    LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit6");
                    return;
                }
            }
            LOGERROR(WARNING,"Failed to find a matching delete ");
            LOGEXIT(INFO,"CSBuilder:handleDeletion - Exit7");
            return;
        }

// ========================================================================
// Deletion handling
// ========================================================================

        SDOXMLString ChangeSummaryBuilder::shiftedIndex(int index)
        {
            int pos = createDeletes[index].value.lastIndexOf('.');
            if (pos < 0) return createDeletes[index].value;

            int val = atoi((const char*)(createDeletes[index].
                value.substring(pos+1)));

            char buf[10];

            sprintf(buf, ".%d", val + createDeletes[index].indexshift);

            return createDeletes[index].value.substring(0,pos) + buf;
        }

// ========================================================================
// Deletion handling
// ========================================================================

        void ChangeSummaryBuilder::shiftIndices(int index, int delta)
        {
            int pos = createDeletes[index].value.lastIndexOf('.');
            if (pos < 0) return;

            SDOXMLString refstring = createDeletes[index].value.substring(0,pos);
            pos = atoi((const char*)(createDeletes[index].value.substring(pos+1)));
                                     
            for (unsigned int k=index+1;k<createDeletes.size();k++)
            {

                if (createDeletes[k].type.equals("create")) continue;

                int pos2 = createDeletes[k].value.lastIndexOf('.');
                if (pos2 < 0) continue;

                SDOXMLString refto = createDeletes[k].value.substring(0,pos2);
                pos2 = atoi((const char*)(createDeletes[k].value.substring(pos2+1)));

                if (refstring.equals(refto) && pos2 >= pos)
                {
                    createDeletes[k].indexshift += delta;
                }
            }
        }

// ========================================================================
// Rebuilding of a change summary from the lists
// ========================================================================

        void ChangeSummaryBuilder::buildChangeSummary(
             DataObjectPtr changeSummaryDO)
        {
            LOGENTRY(INFO,"CSBuilder:buildChangeSummary");

            unsigned int i;

            try {
                if (changeSummaryDO == 0)
                {
                    // there is no summary data object to rebuild
                    LOGEXIT(INFO,"CSBuilder:buildChangeSummary Exit1");
                    return;
                }
                ChangeSummaryImplPtr csi = 
                    staticCast<ChangeSummaryImplPtr>(changeSummaryDO->getChangeSummary());
                if (!csi)
                {
                    // there is no change summary - this shouldnt happen
                    LOGEXIT(INFO,"CSBuilder:buildChangeSummary Exit2");
                    return;
                }

                // add the creations and deletions in order

                for (i = 0; i < createDeletes.size(); i++)
                {

                    LOGINFO_1(INFO,"CSBuilder:create/delete:%s",
                        (const char*)(createDeletes[i].value));

                    SDOXMLString thispath;

                    if (createDeletes[i].indexshift != 0)
                    {
                         thispath = shiftedIndex(i);
                    }
                    else
                    {
                        thispath = createDeletes[i].value;
                    }

                    if (!createDeletes[i].type.equals("delete"))
                    {
                        DataObjectPtr dob;
                        if (thispath.isNull())
                        {
                            dob = rootDataObject;
                        }
                        else
                        {
                            dob = rootDataObject->getDataObject((const char*)thispath);
                        }
                        if (dob != 0) {
                            
                            LOGINFO(INFO,"CSBuilder:add create to change summary");

                            csi->appendToCreations(dob->getContainmentProperty(),
                                            dob, dob->getContainer());
                            shiftIndices(i,-1);
                        }
                        else 
                        {
                            LOGERROR_1(WARNING,"Failed to find object at %s",
                                (const char*)createDeletes[i].value);
                        }
                    }
                    else 
                    {
                        // its a deletion - match it.
                        for (unsigned int j = 0; j < deletions.size(); j++)
                        {
                            if (deletions[j].completedprocessing)continue;

                            if (thispath.equals (deletions[j].reference))
                            {
                                // matching deletion found
                                LOGINFO_1(INFO,"CSBuilder:matched delete:%s",
                                (const char*)deletions[j].reference);
                                
                                handleDeletion(csi, j,createDeletes[i].value);
                                // update the path on any higher items in the current 
                                // many-valued property.
                                shiftIndices(i,1);

                                break;
                            }
                        }
                    }
                }
        
                // add the changes

                std::list<changeAttribute>::iterator a;
                std::list<changeElement>::iterator e;

                for (unsigned i = 0; i< changes.size(); i++)
                {
                    DataObjectPtr dob;
                    if (changes[i].reference.isNull())
                    {
                        dob = rootDataObject;
                    }
                    else 
                    {
                        dob = rootDataObject->getDataObject((const char*)changes[i].reference);
                    }
                    if (dob != 0)
                    {
                        LOGINFO_1(INFO,"Located a change at %s",
                             (const char*)changes[i].reference);

                        // Need to get the changelogitem, and append settings to
                        // it.Append attributes

                        for (a=changes[i].attributes.begin();
                             a != changes[i].attributes.end();++a)
                        {
                            PropertyPtr p = dob->getInstanceProperty((const char*)((*a).name));
                            if (!p) {
                                SDOString msg("Cannot find property: ");
                                msg += (const char*)((*a).name);
                                throw SDOPropertyNotFoundException(TUSCANY_SDO_EINFO,
                                                                   msg.c_str());
                            }
                            if (p->getType().isDataType()) 
                            {
                                csi->appendToChanges(
                                    *p,dob,(*a).value,0);
                            }
                            else
                            {
                                for (unsigned int j = 0; j < deletions.size(); j++)
                                {
                                    if ((*a).value.equals (deletions[j].reference))
                                    {
                                        csi->appendToChanges(
                                                *p,dob,deletions[j].dob,0);
                                        break;
                                    }
                                }
                            }
                        }
                        for (e=changes[i].elements.begin();
                             e != changes[i].elements.end();++e)
                        {
                            PropertyPtr p = dob->getInstanceProperty
                                               ((const char*)((*e).name));
                            if (!p) {
                                SDOString msg("Cannot find property: ");
                                msg += (const char*)((*e).name);
                                throw SDOPropertyNotFoundException(TUSCANY_SDO_EINFO,
                                                                   msg.c_str());
                            }
                            if ((*e).isDeletion)
                            {

                                LOGINFO_1(INFO,"CSBuilder Change was a deletion of:%s",
                                    (const char*)((*e).path));

                                for (unsigned int j = 0; j < deletions.size(); j++)
                                {
                                    if ((*e).path.equals (deletions[j].reference))
                                    {
                                        // matching deletion found
                                        if (deletions[j].dob != 0)
                                        {
                                            csi->appendToChanges(
                                                *p,dob,deletions[j].dob,(*e).index);
                                        }
                                        break;
                                    }
                                }
                            }
                            else if ((*e).isReference)
                            {
                                LOGINFO_2(INFO,"CSBuilder:Change was a reference from:%s to %s" ,
                                    (const char*)((*e).path),
                                    (const char*)((*e).value));

                                DataObjectPtr pdob = rootDataObject->getDataObject((const char*)(*e).value);
                                if (pdob != 0)
                                {
                                    csi->appendToChanges(*p,dob,pdob,(*e).index);
                                }
                            }
                            else
                            {
                                LOGINFO_1(INFO,"CSBuilder:Change was a many valued item:%s" ,
                                (const char*)((*e).name));

                                csi->appendToChanges(*p,dob,(*e).value,(*e).index);
                            }
                        }
                    }
                    else
                    {
                        LOGERROR_1(WARNING,"CSBuilder:Failed to find object at %s",
                            (const char*)(changes[i].reference));
                    }
                }
            }
            catch (SDORuntimeException e)
            {
                LOGSDOEXCEPTION(ERROR,"CSBuilder:Error rebuilding change summary",e);
            }
            LOGEXIT(INFO,"CSBuilder:buildChangeSummary Exit3");
        }
        
// ========================================================================
// Parsing code
// ========================================================================

        void ChangeSummaryBuilder::processStart(
            const SDOXMLString& localname,
            const SDOXMLString& prefix,
            const SDOXMLString& URI,
            const SAX2Namespaces& namespaces,
            const SAX2Attributes& attributes)
        {
            char cbuf[32];
            int i;

            LOGENTRY(INFO,"CSBuilder:processStart");

            if (currentState == baseState)
            {
                if (localname.equalsIgnoreCase("create") ||
                    localname.equalsIgnoreCase("delete"))
                {
                    currentState = dealingWithCreateDelete;
                    // will be expecting a chars message to give the 
                    // contents
            
                    createDeletes.push_back(createDelete(localname));

                    LOGINFO_1(INFO,"CSBuilder: Processing a %s",(const char*)localname);
                    LOGEXIT(INFO,"CSBuilder:processStart Exit1");
                    return;
                }
                else
                {
                    // we are starting a new change record here
                    currentState = dealingWithChange;
                    
                    // we expect the entry to have an sdo:ref, and possibly
                    // some attributes for the changed single-values
                    SDOXMLString ref = attributes.getValue("ref");

                    if (ref.isNull())
                    {
                        LOGERROR(INFO,"CSBuilder:Problem with change reference");
                        ref = SDOXMLString("Unknown");
                    }

                    currentChange = change(localname, ref);

                    LOGINFO_2(INFO,"CSBuilder: Processing a change:%s:%s",
                        (const char*)localname,
                        (const char*)ref);
                
                    currentLocation.clear();

                    if (ref.equals("#/")) 
                    {
                        ref = SDOXMLString("#");
                    }

                    currentLocation.push_back(ref);
                    
                    // clear out the change state - if there was any.

                    changeIndex = 0;
                    previousChange = SDOXMLString("");

                    // now insert any remaining attributes of this change
                    
                    for (i=0; i < attributes.size(); i++)
                    {
                        // push all the attributes into the change record.
                        SDOXMLString name = attributes[i].getName();
                        if (!name.equalsIgnoreCase("ref"))
                        {
                            SDOXMLString value = attributes[i].getValue();
                            currentChange.addAttribute(
                                changeAttribute(name,value));
                        }
                    }
                }
                LOGEXIT(INFO,"CSBuilder:processStart Exit2");
                return;
            }

            // We are not in the base state - we could be in a change or a deletion
            // We are now dropping into an element, which could be either a many-valued
            // primitive type, or a placeholder in list of deletions, or a deletion
            // record.

            if (currentState == dealingWithChange)
            {

                if (!previousChange.equals(localname))
                {
                    changeIndex = 0;
                    previousChange = localname;
                }
                else
                {
                    changeIndex++;
                }


                sprintf(cbuf,"%d",changeIndex);
                currentLocation.push_back(localname + "." + cbuf);

                // build the current location...
                SDOXMLString curr_loc = "";

                if (currentLocation.size() > 0)
                {
                    for (unsigned int pi = 0; pi < currentLocation.size()-1; pi++)
                    {
                        curr_loc  = curr_loc + currentLocation[pi];
                        curr_loc = curr_loc + "/";
                    }
                    curr_loc = curr_loc + currentLocation[currentLocation.size()-1];
                }

                LOGINFO_1(INFO,"CSBuilder: Dropping into an element of a change:%s", 
                        (const char*)localname);
                LOGINFO_1(INFO,"CSBuilder:currentLocation:%s",
                        (const char*)curr_loc);

                // First case - its a placeholder for an element in a list
                // all we have to do is keep track of the list index.

                if (attributes.size() == 1)
                {
                    SDOXMLString name = attributes[0].getName();
                    if (name.equalsIgnoreCase("ref"))
                    {
                        currentState = dealingWithChangeElement;
                        currentChange.addElement(
                                    changeElement(localname, curr_loc,
                                    attributes[0].getValue(), true, false));

                        LOGEXIT(INFO,"CSBuilder:processStart Exit3");
                        return; 
                    }
                }

                if (attributes.size() == 0)
                {
                    // It could be a many-valued primitive, or 
                    // a deletion with no attributes
                    // can we match the current path to a known deletion?

                    bool isDelete = false;

                    for (unsigned int i = 0; i < createDeletes.size(); i++)
                    {
                        if (createDeletes[i].type.equals("delete"))
                        {
                            if (curr_loc.equals(createDeletes[i].value))
                            {
                                LOGINFO(INFO,"CSBuilder: An existing delete was found");

                                isDelete = true;
                                break; // out of the for loop
                            }
                        }
                    }
                    if (!isDelete) 
                    {
                        LOGINFO(INFO,"CSBuilder: Change with no atts and not deletion- an element");
                        
                        // and will get picked up by the 'characters' method.
                        currentState = dealingWithChangeElement;
                        currentLocalName = SDOXMLString(localname);

                        LOGEXIT(INFO,"CSBuilder:processStart Exit4");
                        return;
                    }
                }

                currentChange.addElement(changeElement(localname, curr_loc, false, true));
                currentDeletion = deletion(localname, curr_loc);
                
                LOGINFO_2(INFO,"CSBuilder: Found a deletion:%s:%s",
                    (const char*)localname,
                    (const char*)curr_loc);
                
                    
                // clear out the change state - if there was any.
                deletionLevel = 0;
                deletionIndex = 0;
                previousDeletion = SDOXMLString("");

                currentState = dealingWithDeletion;

                // Now add the attributes if there are any.

                for (int i=0; i < attributes.size(); i++)
                {
                    // push all the attributes into the deletion record.
                    SDOXMLString name = attributes[i].getName();
                    if (!name.equalsIgnoreCase("ref"))
                    {
                        SDOXMLString value = attributes[i].getValue();
                        currentDeletion.addAttribute(
                            deletionAttribute(name,value));
                    }
                }

                LOGEXIT(INFO,"CSBuilder:processStart Exit5");
                return;
            }
            if (currentState == dealingWithDeletion)
            {
                // we are already in a deletion, and entering an element 
                if (!previousDeletion.equals(localname))
                {
                    deletionIndex = 0;
                    previousDeletion = localname;
                }
                else
                {
                    deletionIndex++;
                }

                sprintf(cbuf,"%d",deletionIndex);
                currentLocation.push_back(localname + "." + cbuf);

                 // First case - its a placeholder for an element in a list
                // all we have to do is keep track of the list index.

                if (attributes.size() == 1)
                {
                    SDOXMLString name = attributes[0].getName();
                    if (name.equalsIgnoreCase("ref"))
                    {
                        LOGINFO(INFO,"CSBuilder: The element is a reference to an existing entry in a list");
                        
                        currentState = dealingWithDeletionElement;
                        
                        LOGEXIT(INFO,"CSBuilder:processStart Exit6");
                        return; 
                    }
                }

                SDOXMLString curr_loc = "";
                if (currentLocation.size() > 0)
                {
                    for (unsigned int pi = 0; pi < currentLocation.size()-1; pi++)
                    {
                        curr_loc  = curr_loc + currentLocation[pi];
                        curr_loc = curr_loc + "/";
                    }
                    curr_loc = curr_loc + currentLocation[currentLocation.size()-1];
                }

                if (attributes.size() == 0)
                {
                    // It could be a many-valued primitive, or 
                    // a deletion with no attributes
                    // can we match the current path to a known deletion?
                    bool isDelete = false;

                    for (unsigned int i = 0; i < createDeletes.size(); i++)
                    {
                        if (createDeletes[i].type.equals("delete"))
                        {
                            if (curr_loc.equals(createDeletes[i].value))
                            {
                                LOGINFO(INFO,"CSBuilder: An existing delete was found - its a delete");

                                isDelete = true;
                                break; // out of the for loop
                            }
                        }
                    }
                    if (!isDelete) 
                    {
                        LOGINFO(INFO,"CSBuilder: Found a change with no atts, and no deletion matches - its an element");
                        
                        // and will get picked up by the 'characters' method.
                        currentState = dealingWithDeletionElement;
                        
                        LOGEXIT(INFO,"CSBuilder:processStart Exit7");
                        return;
                    }
                }


                // We are starting a new deletion record

                LOGINFO_2(INFO,"CSBuilder: Found a deletion within a deletion:%s:%s",
                    (const char*)localname, (const char*)curr_loc);
                    
                deletionList.push_back(deletionListElement(currentDeletion,deletionIndex,
                previousDeletion));
                                
                currentDeletion = deletion(localname, curr_loc);

                // we are in a nested delete - so record the level such that we can pop state
                // from the deletionList on coming back out of this.

                deletionLevel++;
                //deletionIndex = 0;
                previousDeletion = SDOXMLString("");

                currentState = dealingWithDeletion;

                // Now add the attributes if there are any.

                for (int i=0; i < attributes.size(); i++)
                {
                    // push all the attributes into the deletion record.
                    SDOXMLString name = attributes[i].getName();
                    if (!name.equalsIgnoreCase("ref"))
                    {
                        SDOXMLString value = attributes[i].getValue();
                        currentDeletion.addAttribute(
                            deletionAttribute(name,value));
                    }
                }

                LOGEXIT(INFO,"CSBuilder:processStart Exit8");
                return;

            }
        }
        
// ========================================================================
// Parser ends an element
// ========================================================================
        
        void ChangeSummaryBuilder::processEnd(
            const SDOXMLString& localname,
            const SDOXMLString& prefix,
            const SDOXMLString& URI)
        {

            LOGENTRY(INFO,"CSBuilder:processEnd");

            if (currentState == dealingWithCreateDelete)
            {
                // its already been completed by the characters message
                currentState = baseState;
                LOGEXIT(INFO,"CSBuilder:processEnd Exit1");
                return;
            }
            else if (currentState == dealingWithChangeElement)
            {
                // its already been completed by the characters message
                currentState = dealingWithChange;
                currentLocation.pop_back();
                LOGEXIT(INFO,"CSBuilder:processEnd Exit2");
                return;
            }
            else if (currentState == dealingWithDeletionElement)
            {
                // its already been completed by the characters message
                currentState = dealingWithDeletion;
                currentLocation.pop_back();
                LOGEXIT(INFO,"CSBuilder:processEnd Exit3");
                return;
            }
            else if (currentState == dealingWithChange)
            {
                // we should have finished, there are no nested changes
                currentState = baseState;
                changes.push_back(currentChange);
                currentLocation.clear();
                LOGEXIT(INFO,"CSBuilder:processEnd Exit4");
                return;
            }
            else if (currentState == dealingWithDeletion)
            {
                currentLocation.pop_back();
                deletions.push_back(currentDeletion);
                if (deletionLevel > 0)
                {
                    deletionLevel--;
                    if (deletionList.size() > 0)
                    {
                        int index = deletionIndex;
                        SDOXMLString path = currentDeletion.reference;
                        SDOXMLString name = currentDeletion.name;

                        deletionListElement dl = deletionList[deletionList.size()-1]; 
                        currentDeletion = dl.del;
                        deletionIndex = dl.index;
                        previousDeletion = dl.previous;
                        deletionList.pop_back();
                        // and insert the inside deletion in the outside one.
                        currentDeletion.insertElement(deletionElement(
                            name, path,index));
                    }

                    LOGEXIT(INFO,"CSBuilder:processEnd Exit5");
                    return;
                }
                else
                {
                    currentState = dealingWithChange;

                    LOGEXIT(INFO,"CSBuilder:processEnd Exit6");
                    return;
                }
            }
        }

// ========================================================================
// Parse characters
// ========================================================================
        
        
        void ChangeSummaryBuilder::processChars(
            const SDOXMLString& chars)
        {
            LOGENTRY(INFO,"CSBuilder:processChars");

            if (currentState == dealingWithCreateDelete)
            {
                // this is text within a create/delete indicating the 
                // value of the item deleted/created.
                if (createDeletes.size() > 0) 
                {
                    createDeletes[createDeletes.size()-1].value = chars;
                    LOGINFO_1(INFO,"CSBuilder:characters create/delete:%s",
                        (const char*)chars);
                }
                LOGEXIT(INFO,"CSBuilder:processChars Exit1");
                return;
            }
            else if (currentState == dealingWithChangeElement)
            {
                changeElement ce = changeElement(currentLocalName, previousChange, false, false);
                ce.value = chars;
                ce.index = changeIndex;
                currentChange.addElement(ce);

                LOGINFO_1(INFO,"CSBuilder: Characters in a change element:%s",
                    (const char *)chars); 
                
                LOGEXIT(INFO,"CSBuilder:processChars Exit2");
                return;
            }
            else if (currentState == dealingWithDeletionElement)
            {
                deletionElement ce = deletionElement(previousDeletion);
                ce.value = chars;
                ce.index = deletionIndex;
                currentDeletion.addElement(ce);
                
                LOGINFO_1(INFO,"CSBuilder: Characters in a deletion element:%s",
                    (const char *)chars); 

                LOGEXIT(INFO,"CSBuilder:processChars Exit3");
                return;
            }

            LOGINFO_1(INFO,"CSBuilder: Characters in nothing!!:%s",
                (const char *)chars); 
            LOGEXIT(INFO,"CSBuilder:processChars Exit4");
        }

    
// ========================================================================
// Classes holding temporary parsed information
// ========================================================================
        

        /////////////////////////////////////////////////
        // An element in the list of creates/deletes
        /////////////////////////////////////////////////

        createDelete::createDelete()
        {
        }

        createDelete::createDelete(SDOXMLString intype) :
        type(intype)
        {
            indexshift = 0;
        }

        createDelete::~createDelete()
        {
        }

        /////////////////////////////////////////////////
        // An element in the list of changes
        /////////////////////////////////////////////////

        changeAttribute::changeAttribute()
        {
        }

        changeAttribute::changeAttribute(SDOXMLString inname,
            SDOXMLString invalue): name(inname), value(invalue)
        {
        }

        changeAttribute::~changeAttribute()
        {
        }

        changeElement::changeElement()
        {
        }

        changeElement::changeElement(SDOXMLString inname, 
            SDOXMLString inpath, bool isRef, bool isDel)
            : name(inname) , path(inpath)
        {
            isReference=isRef;
            isDeletion= isDel;
        }

        changeElement::changeElement(SDOXMLString inname, 
            SDOXMLString inpath,
            SDOXMLString invalue, bool isRef, bool isDel)
            : name(inname), value(invalue), path(inpath)
        {
            isReference=isRef;
            isDeletion= isDel;
        }

        changeElement::~changeElement()
        {
        }

        change::change()
        {
        }

        change::change(SDOXMLString inname, SDOXMLString ref): 
                name(inname), reference(ref)
        {
        }

        void change::addAttribute(changeAttribute ca)
        {
            attributes.push_back(ca);
        }

        void change::addElement(changeElement ce)
        {
            elements.push_back(ce);
        }

        /////////////////////////////////////////////////
        // An element in the list of deletions
        /////////////////////////////////////////////////

        deletionAttribute::deletionAttribute()
        {
        }

        deletionAttribute::deletionAttribute(SDOXMLString inname,
            SDOXMLString invalue): name(inname), value(invalue)
        {
        }

        deletionAttribute::~deletionAttribute()
        {
        }

        deletionElement::deletionElement()
        {
        }

        deletionElement::deletionElement(SDOXMLString inname)
            : name(inname)
        {
            isDeletion = false;
        }

        deletionElement::deletionElement(SDOXMLString inname,
            SDOXMLString inpath, int inindex)
            : name(inname), value(inpath), index(inindex)
        {
            isDeletion = true;
        }

        deletionElement::~deletionElement()
        {
        }

        deletion::deletion()
        {
        }

        deletion::deletion(SDOXMLString inname, SDOXMLString ref): 
                name(inname), reference(ref)
        {
            completedprocessing=false;
        }

        void deletion::addAttribute(deletionAttribute ca)
        {
            attributes.push_back(ca);
        }

        void deletion::addElement(deletionElement ce)
        {
            elements.push_back(ce);
        }

        void deletion::insertElement(deletionElement ce)
        {
            elements.insert(elements.begin(),ce);
        }

        deletionListElement::deletionListElement()
        {
        }

        deletionListElement::deletionListElement(
            deletion in_del, 
            int in_index, 
            SDOXMLString in_prev):
            del(in_del), index(in_index), previous(in_prev)
        {
        }
        
    } // End - namespace sdo
} // End - namespace commonj

