/* OdmWorking100.cpp 0.08            UTF-8                   dh:2007-01-19
 *
 *             OdmWorking100 CLASSES AND INTERFACE IMPLEMENTATIONS
 *             ***************************************************
 *
 * This is the implementation of the OdmWorking100 class and the COM
 * object it delivers.
 *
 * This module is defined for separate compilation and inclusion as library
 * code along with other OdmNative100 object viles.  It works for
 * OdmWorking100.obj to be included in a static library as well.  It is
 * not expected that the factory function, OdmBindWorking100, be usable
 * as a public function.  Because of complex preconditions, it should only
 * be called from OdmNative100.obj or another private module of the
 * OdmNative implementation.
 *
 * FIXME: This module, and the corresponding header, will be the subject
 *        of some serious refactoring.  That refactoring will eliminate
 *        the delivery of ODMSTATUS results and consolidate standard usage
 *        operations into single methods on the lines of the practical100
 *        interfaces in ODMJNI 1.0.
 *
 *
 *                       Copyright © 2006-2007 NuovoDoc
 *                           <http://NuovoDoc.com>
 *
 *              This software is released under the Open
 *              Document Management API License 1.0, an open-
 *              source software license modeled on the BSD
 *              License template.
 *
 *              A copy of the license should accompany the
 *              source code that includes this file.  If the
 *              license has has been separated from the code,
 *              obtain a copy at <http://DMware.info/license/>.
 */

#define OdmWorking100_C_Version_ "0.08 " __FILE__ " " __TIMESTAMP__

    /* A consistent format for building descriptive texts that
       identify specific implementations.
       */


#include <windows.h>

#include <new>
    /* For std::nothrow, preventing exceptions being thrown from
       OdmBindWorking100.
       */

#include "odma32types100.h"
    /* For data types and constants applicable to ODMA 1.0 access and
       important for Odma32api.h too.
       */

#include "Odma32api100.h"
    /* For entry points into the ODMA Connection Manager for ODMA 1.0
       functions.
       */

#include "OdmNative100i.hpp"
    /* For the private IodmNative100i interface used in operation
       of OdmWorking100. */


#include "OdmWorking100.hpp"
    /* For the interfaces and functions that are defined in this module. */

#include "OdmBindWorking100.hpp"
    /* To confirm against the factory function defined here. */




/*               ODMWORKING CLASS DEFINITION AND IMPLEMENTATION
                 ********************************************** */

class OdmWorking : public IodmWorking100
{ /* Class implemented internal to OdmWorking100.cpp
     This class is defined privately and used for the
     current implementation of the IodmWorking100
     COM object and interface.
     */

  private:
    volatile LONG refCount;
        /* Used to count the number of outstanding references to
           interfaces of the object */

    IodmApplication100i *pIodmApp;
        /* The interface of the OdmNative100 application object that
           is the connection of this object to the ODMA API and the
           DMS handle to be used in all of our ODMA operations.
           The hConMan and hOdmWorking values here are only valid so
           long as this interface is held.  It should not be given up
           until IodmWorking100::release() or the constructor fails.
           */

    HINSTANCE hConMan;
        /* Cached value from pIodmApp->hConManLib()
           The constructor for this class will fail if this value is
           NULL, the same as for hWorking being NULL.*/

    ODMHANDLE hWorking;
        /* Cached value from pIodmApp->hWorkingDMS().  If there is
           no such value, the constructor will fail such that we are
           not holding any resources and our QueryInterface will fail for
           all entries.  This causes the factory function to delete
           the newly-constructed instance.
           */

    BOOL onlyViewable;
        /* TRUE when the document implemented with this object is
           only Viewable.  This only impacts the commitChanges
           behavior and is important for applications knowing
           whether a cache of document properties should be invalidated. */


    char curDocId[ODM_DOCID_MAX];
        /* The ODMA Document ID for the alread-opened ODMA Document that
           this object represents with ODMA and to the ODMA-aware
           application.  This array can change as the result of
           a commitChanges operation.
              XXX: This data is assumed to have been validated before
                   ever being offered to the constructor.  There is
                   minimal defensive code here.
           */

    char curDocLocation[ODM_FILENAME_MAX];
        /* The Document Location for the exchange location to be used
           between the application and the DMS for the "open" DMS document.

           The ODMA definition of ODM_FILENAME_MAX is slightly less than
           the Win32 definition.
           */


  public:
    OdmWorking(    IUnknown  *pUnkApp,    /* Interface to the Application
                                             object that is logically our
                                             container and that we must not
                                             outlive. */



                 const char  pszDocId[],  /* The initial Document ID with
                                             which this document instance is
                                             to be associated. */

                 const char  pszDocLoc[], /* The already-"opened" Document
                                             Location that is used until
                                             the working document is closed
                                             as part of release, provided
                                             that the factory function is
                                             successful. */

                       BOOL  viewMode     /* Whether viewOnly is desired
                                             or not. */
                 );
        /* This constructor is appropriate following a SelectDoc and an
           OpenDoc that obtains a document location for access to the
           content document file.

           Until a successful OdmWorking::QueryInterface, this class is
           not holding any references to resources, has reference count 0,
           and can be deleted at will.

           XXX: It is *CRITICAL* that the factory function for an OdmWorking
                instance perform a successful OdmWorking::QueryInterface
                while the pUnkApp provided to this constructor is still
                available to the factory function.  The resulting interface
                should be supplied to the caller of the factory function.

                If the factory function fails to obtain a successful
                OdmWorking::QueryInterface, it must delete the constructed
                object and should not use any of its methods, because the
                object and its interface are not valid.
           */


    /* These method declarations identify those virtual methods that
       are overloaded for instances of this class.  The methods
       are defined out-of-line with the OdmWorking::<<method>>
       definitions following the class declaration, below. */


    /*  IUnknown Methods */
    virtual HRESULT WINAPI QueryInterface(REFIID rIID, void** ppv);
    virtual ULONG WINAPI AddRef(void);
    virtual ULONG WINAPI Release(void);

    /*  IodmWorking100 Methods */
    virtual LPCSTR WINAPI interfaceImplementation(void);
    virtual BOOL WINAPI viewOnly(void);
    virtual LPCSTR WINAPI docID(void);
    virtual LPCSTR WINAPI docLocation(void);
    virtual ODMSTATUS WINAPI docProperty(WORD propnum, LPSTR lpszData,
                                         WORD dataLen);
    virtual ODMSTATUS WINAPI deriveNewDoc(LPCSTR docFormatName,
                                           LPSTR pszDocID);
    virtual HRESULT WINAPI openPendingDoc(LPCSTR pszDocID,
                                          REFIID pIID,
                                            void **ppIface);
    virtual BOOL WINAPI commitChanges(void);


    }; /* class OdmWorking */



static const IID IID_IodmApplication100i = IID_IodmApplication100i_ ;
    /* IodmApplication100i IID of the interface we want to hold in our
       *pIodmApp */



OdmWorking::OdmWorking
              (    IUnknown  *pUnkApp,    /* Interface to the Application
                                             object that is logically our
                                             container and that we must not
                                             outlive. */

                 const char  pszDocId[],  /* The initial Document ID with
                                             which this document instance is
                                             to be associated. */

                 const char  pszDocLoc[], /* The docLocation for the
                                             already-opened Document ID. */

                       BOOL  viewMode     /* Whether viewOnly is desired
                                             or not. */
                 )
              : refCount(0),
                pIodmApp(NULL), hConMan(NULL), hWorking(NULL),
                onlyViewable(FALSE)

{   /* This is a touchy constructor.  If we fail, hWorking is NULL and there
       are no resources held onto.  If we succeed, hWorking is non-NULL but
       pIodmApp is not held.

       When QueryInterface succeeds completely for the first time, and only
       the first time, pIodmApp will be held.  Otherwise, it will never be
       held.   That way, a delete of the unreferenced object will not result
       in a memory leak caused by an unreleased pIodmApp.

       No QueryInterface that finds a NULL hWorking will ever succeed.

       It is critical that the factory function perform the first
       QueryInterface while the pUnkApp parameter is still for a globally-
       held interface. If that QueryInterface fails, the factory function
       must delete the OdmWorking object without ever delivering any
       references outside of the factory function.
       */


    if(pUnkApp == NULL || pszDocId == NULL || pszDocLoc == NULL) return;
        /* If any of our parameters are ill-formed, leave now, letting our
           object be still-born. */


    char *dest = &curDocId[0];
    int  docGuard = sizeof(curDocId);

    while ( *dest++ = *pszDocId++)
          if (!--docGuard) return;
        /* Save the Document ID, copying up to and including the '\0'.
           If we fill curDocId and haven't seen the '\0', bail out.
           */

    dest = &curDocLocation[0];
    docGuard = sizeof(curDocLocation);

    while ( *dest++ = *pszDocLoc++)
          if (!--docGuard) return;
        /* Save the Document Location, copying up to and including the '\0'.
           And if we fill curDocLocation without seeing the '\0' here, we
           will also bail out.
           */

    /* We perform just enough defense to preserve the integrity of our
       instance data.  Other than that, we expect our factory to always
       protect us and its caller from ever being delivered an interface
       to an ill-constructed OdmWorking instance.
       */


    if (  S_OK != pUnkApp->QueryInterface( IID_IodmApplication100i,
                                               (void**) &pIodmApp )
          )
         return;
            /* No interface, so nothing to do at all because we don't
               have any way to set hConMan and hWorking.  Fail safe. */

    pIodmApp -> Release();
        /* We have the interface, but we can't hold it until we
           have produced an interface for someone to hold onto us
           with.  Even though we have not reserved it, we can safely
           use it until we exit from this constructor.

           XXX: This is a dangerous act.  The factory function that
                constructs this class must obtain a successful
                OdmWorking::QueryInterface result or it must delete this
                object so that the pIodmApp pointer is never invalid while
                this object exists.
           */


    hConMan = pIodmApp->hConManLib();
    if (  hConMan == NULL  )
         return;
            /* We're busted without a Connection Manager.  Fail safe. */

    hWorking = pIodmApp->hWorkingDMS();
    if (  hWorking == NULL )
         return;
            /* And crushed without a Working DMS.  Fail safe here too. */

    /* hWorking is now non-null and we are good to go.  It is left to
       the factory function to either provide a good Working::QueryInterface
       result for its caller or else delete this constructed instance.
       */

    onlyViewable = (viewMode != FALSE);
            /* Remember the viewMode, at last. */

    } /* OdmWorking::OdmWorking */



static const IID IID_IodmUnknown = IID_IodmUnknown_ ;
    /* Standard IUnknown IID uniquelly named to avoid conflicts. */

static const IID IID_IodmWorking100 = IID_IodmWorking100_ ;
    /* IodmWorking100 IID of the current header level for this object's
       interface.  These are the only IIDs recognized by the OdmWorking::
       QueryInterface.  */


HRESULT WINAPI
    OdmWorking::QueryInterface(REFIID rIID, void **ppIface)

{   /* Simple single-inheritance implementation. */

    if (ppIface != NULL)
         *ppIface = NULL;
    else return E_POINTER;

    if (&rIID == NULL)
         return E_POINTER;
            /* Never store into a null pointer.  Never reference
               through a null pointer.  We are not cycling these
               QueryInterfaces so hard that we are pained by this
               amount of defensive programming.

               The OdmWorking constructor and the OdmBindWorking100
               factory function depend on this code providing a meaningful
               defense against misadventures.
               */

    if (hWorking == NULL)
         return E_FAIL;

    if (  !(rIID == IID_IodmWorking100)
             && !(rIID == IID_IodmUnknown)
          )
        return E_NOINTERFACE;

    /* We are about to succeed. */

    if (refCount == 0)
         pIodmApp->AddRef();
            /* We are in business.  Hold pIodmApp in a vice-like grip
               until we are released. */

    *ppIface = (IodmWorking100 *) this;
        /* relying on the fact of single inheritance to satisfy all
           requests with the same binary interface.
           */

    ((IUnknown *)(*ppIface)) -> AddRef();
        /* Count the interface reference we have just given out */

    return S_OK;

    } /* OdmWorking::QueryInterface */



ULONG WINAPI
    OdmWorking::AddRef(void)

{   /* Simple reference-count operation.
       XXX: If this were to wrap around, it's not clear
         that there is anything useful to be done about it.
       */

    return InterlockedIncrement(&refCount);
        /* Maintain thread-safety even though most
           operations should all be done on a single
           GUI thread.
           */
    } /* OdmWorking::AddRef */



ULONG WINAPI
    OdmWorking::Release(void)

{   /* This method is never called unless an interface to us has
       been issued.  That means the constructor succeeded and
       QueryInterface reserved pI0dmApp.
       */

    if (InterlockedDecrement(&refCount))
         return 1;
            /* Note that the object can be deleted by another
               release before this one returns.  No accesses
               to anything are assured following a decrement that
               did not reach 0.
               */

    /* This thread has just reduced the reference count to 0.  Our
       this pointer is good until we delete our own instance.
       */


    if (hWorking != NULL)
         { /* Close the document with the DMS */

           /* Get the entry point for OdmCloseDoc.
              This is the only place where we need this entry.
              */

           pfnODMCLOSEDOC
                pOdmCloseDoc
                    = (pfnODMCLOSEDOC)
                            GetProcAddress(hConMan, "ODMCloseDoc");
                            /* Entry point for ODMCloseDoc */

           if (pOdmCloseDoc == NULL)
                { /* We have a document we can't close.
                     XXX: We are in a world of hurting and can't do
                          much at this point.  This would appear to be a
                          loggable exception, but we really don't want to
                          get into anything that could have this release
                          fail.

                          The last hope is that the Application's
                          UnRegisterApp will provoke the DMS into cleaning
                          up for us.
                     */
                  }

           else { /* Close the Document
                     */

                  ODMSTATUS rc = pOdmCloseDoc
                                    (hWorking,
                                     curDocId,
                                     0xFFFFFFFF, /* Unknown editing time */
                                     0xFFFFFFFF, /* Unknown pages printed */
                                     NULL,       /* No application-specific
                                                    data record */
                                     0
                                     );

                      /* XXX: There is nothing for us to do if the Close
                              fails.  It is too late and there is no way
                              to let the application know.  There should
                              be no lost work because the commitChanges
                              either succeeded or it failed and the
                              application took whatever fall-back measures
                              it is designed for.
                         */
                  }

           }



    if(pIodmApp != NULL)
         { /* Release the OdmApplication interface */

           pIodmApp -> Release();

           }


    /* Con Te Partirò */

    delete this;
        /* When we are the final release, there can be no
           others and *this is ours to delete.  Afterwards,
           no references to the object fields and members
           are valid.
           */

    return 0;

    } /* OdmWorking::Release */



LPCSTR WINAPI
    OdmWorking::interfaceImplementation(void)

{   /* Identify the interface definitions followed by the implementation.
       */

    return "defined in " OdmWorking100_H_Version_ ",\n"
           "implemented in " OdmWorking100_C_Version_ ",\n"
           #if defined(_MSC_EXTENSIONS)
                "MS-extended "
           #endif
           #if defined(__cplusplus)
                "C++"
           #elif defined(__STDC__)
                "Standard C"
           #elif defined(_MSC_VER)
                "Visual C"
           #else
                "C Language"
           #endif

           #if defined(_DLL)
                " DLL"
           #endif
           #if defined(_WIN32)
                " (Win32)"
           #endif
           "compiled " __DATE__ " at " __TIME__
           ;

    } /* OdmWorking::interfaceImplementation */



BOOL WINAPI
    OdmWorking::viewOnly(void)

{   /* We just report the fixed value that we started with. */

    return onlyViewable;

    } /* OdmWorking::viewOnly */



LPCSTR WINAPI
    OdmWorking::docID(void)

{   /* Give out our address for the current Document ID.  The requester
       must not hold that pointer for any longer than the validity of its
       reference to this object's interface.
       */

    if (hWorking != NULL)
         return &curDocId[0];
    else return NULL;

    } /* OdmWorking::docID */



LPCSTR WINAPI
    OdmWorking::docLocation(void)

{   /* Give out our address for the current docLocation.  The requester
       must not hold that pointer for any longer than the validity of its
       reference to this instance's interface.
       */

    if (hWorking != NULL)
         return &curDocLocation[0];
    else return NULL;

    } /* OdmWorking::docLocation */


ODMSTATUS WINAPI
    OdmWorking::docProperty(   WORD  propNum,  /* number of the property */
                              LPSTR  pszData,  /* location for storing
                                                  the property value */
                               WORD  dataLen   /* number of octets available
                                                  for storing the '\0'-
                                                  terminated property value
                                                  */
                              )

{   /* The property value is returned by using the ODMA API directly,
       but filtered to allow only the ODMA 1.0 properties that we support
       in OdmNative100.
       */

    if (pszData != NULL && dataLen > 0) pszData[0] = '\0';

    if (hWorking == NULL || pszData == NULL || dataLen < 2)
         return ODM_E_FAIL;

    switch (propNum)
    {   case     ODM_TITLETEXT:
        case        ODM_AUTHOR:
        case          ODM_NAME:
        case          ODM_TYPE:
        case ODM_CONTENTFORMAT:
                            break;
        default:
                            return ODM_E_ITEM;
        }


    pfnODMGETDOCINFO
        pOdmGetDocInfo
            = (pfnODMGETDOCINFO)
                    GetProcAddress(hConMan, "ODMGetDocInfo");

    if (pOdmGetDocInfo == NULL)
         return ODM_E_FAIL;
                /* We can't access the GetDocInfo function so we
                   simply fail. */

    ODMSTATUS
        rc = (pOdmGetDocInfo)
                (  hWorking, curDocId, propNum, pszData, dataLen  );

    switch (rc)
    {   case ODM_SUCCESS:
        case  ODM_E_FAIL:
        case  ODM_E_ITEM:
                    return rc;
        }

    return ODM_E_FAIL;
        /* For anything but the three ODMSTATUS values we implement. */

    } /* OdmWorking::docProperty */



ODMSTATUS WINAPI
    OdmWorking::deriveNewDoc(  LPCSTR docFormatName,
                                LPSTR pszDocId
                                )

{   /* We use ODMSave as to determine the DocId of a document that is
       transfered into existence.  This is separate here because it
       may return an ODMSTATUS that ends the transferToNewDocument process.
       FIXME: This function is not named correctly.  It should have a name
              like deriveTransferDoc.  I need to fix that now.
              Well, maybe not.  No one else sees this, and NewDoc is not
              used above the OdmNative interfaces.  Think about a better
              name to use when we get around to refactoring all of this
              stuff and this function is no longer public.
       */

    if (   docFormatName == NULL
              || pszDocId == NULL
              || pIodmApp == NULL
           )
         return ODM_E_FAIL;

    pfnODMSAVEAS pODMSaveAs
        = (pfnODMSAVEAS) GetProcAddress(hConMan, "ODMSaveAs");
                /* FIXME: This operation should be cached in OdmNative
                          so that redundant GetProcAddress operations
                          are not performed.
                   */

    if (pODMSaveAs == NULL)
         return ODM_E_FAIL;

    char newDocId[ODM_DOCID_MAX+10] = {'\0'};
        /* Add a little extra padding for over-run protection
           FIXME: We might need stronger defenses against a defective
                  or malicious DMS
           */

    ODMSTATUS rc = pODMSaveAs(   hWorking,
                                 curDocId, newDocId, docFormatName,
                                 NULL, NULL
                                 );

    if (rc != ODM_SUCCESS)
         return rc;

    if (newDocId[ODM_DOCID_MAX-1] != '\0')
         return ODM_E_FAIL;
                /* A modest level of protection even if we
                   don't return this string. */

    if (newDocId[0] == '\0')
         return ODM_E_CANCEL;
                /* We can't use the existing document ID, which is already
                   open, as the document ID of an additional document.
                   A commitChanges on the current document is called for.
                   */

    char *src = newDocId;
    while (*pszDocId++ = *src++) ;
       /* XXX: This transfer is safe so long as pszDocId[] is big
               enough.  We avoid using the string library because
               we intend to keep use in DLLs as small as possible, with
               only essential static runtime library dependencies.
               */

    return ODM_SUCCESS;


    } /* OdmWorking::deriveNewDoc */


HRESULT WINAPI
    OdmWorking::openPendingDoc(  LPCSTR pszDocId,
                                 REFIID rIID,
                                   void **ppIface
                                 )

{   /* This is the other part of transferToNewDocument functionality.
       Although it is technically not needed here, it is included
       because it allows the user of an IodmWorking100 interface to
       implement that function without having an IodmApplication100
       interface to use instead.

       It is assumed that the given DocId is one successfully obtained
       from deriveNewDoc.  It makes no sense to use this function on
       an existing, non-provisional document.
       */

    if (ppIface != NULL)
         *ppIface = NULL;
    else return E_POINTER;

    if (pszDocId == NULL || &rIID == NULL)
         return E_POINTER;

    if (pIodmApp == NULL)
         return E_FAIL;

    char newDocLoc[ODM_FILENAME_MAX] = {'\0'};

    ODMSTATUS OdmStatus
        = pIodmApp -> openDoc(pszDocId, newDocLoc, FALSE);

        /* get a modifiable document location to use. */

    if (OdmStatus != ODM_SUCCESS)
        return E_FAIL;
            /* broken glass everywhere */

    return pIodmApp -> makePendingDocument(pszDocId, newDocLoc,
                                           rIID, ppIface);

    } /* OdmWorking::openPendingDoc */



BOOL WINAPI
    OdmWorking::commitChanges(void)

{   /* If we are operating successfully, we perform the operations
       that are important as part of a commitChanges.  If there is
       any failure at any point of the way, we simply return FALSE.
       */

    if (hWorking == NULL || onlyViewable == TRUE)
         return FALSE;

    pfnODMSAVEDOC pODMSaveDoc
        = (pfnODMSAVEDOC) GetProcAddress(hConMan, "ODMSaveDoc");

    if (pODMSaveDoc == NULL)
         return FALSE;

    char newDocId[sizeof(curDocId)+2]={'\0'};
    /* XXX: Simple use of additional guard nulls to defang buffer-overflow
            edge case.  Add more guard nulls if we are concerned about
            this as a threat.
       XXX: This function depends on the entire array being filled with
            '\0' in this initialization, in accordance with the C++
            standard.
       */

    ODMSTATUS rc = pODMSaveDoc(hWorking, curDocId, newDocId);

    if (rc != ODM_SUCCESS)
         return FALSE;

    if (newDocId[sizeof(curDocId)-1] !='\0' || newDocId[0] == '\0')
         return FALSE;

    /* We are assured that newDocId fits into newDocId, including
       the terminal '\0'.

       XXX: We know the newDocId is not an empty string but we don't know
            that it is well-formed.
       */

    char *dest = curDocId;
    char *source = newDocId;

    while (*dest++ = *source++) ;

    return TRUE;


    } /* OdmWorking::commitChanges */



/*                     ODMBINDWORKING100 FACTORY FUNCTION
                       ********************************** */

    /* FIXME: Add validation functions for the parameters and data
              elements accepted by OdmBindWorking100.  We will add
              defensive code later.
              */


/*                OdmBindWorking100: BIND AN OdmWorking OBJECT
                  ******************************************** */

HRESULT WINAPI
    OdmBindWorking100(    void *pIodmApp,
                                    /* Reference to an IodmApplication100
                                       interface that is valid for at
                                       least until OdmBinding100 returns.
                                       */
                        LPCSTR pszDocId,
                                    /* A legitimate ODMA Document ID for
                                       an open DMS document
                                       */
                        LPCSTR pszDocLoc,
                                    /* The valid ODMA Document Location for
                                       accessing the pszDocId[] document
                                       content as a file.
                                       */
                          BOOL viewMode,
                                    /* True for a viewOnly document */

                           REFIID  rIID,
                                       /* The requested interface */

                             void  **ppIface
                                       /* The location to receive the
                                          Interface pointer or NULL */
                       )

{   /* Instantiate the interface implementation that we have at this
       point.  Technically, this is a method of OdmApplication, but we
       have separated it for manageability reasons.
       */

    /* FIXME: There need to be parameter validations, although we can
              count on OdmWorking and OdmWorking::QueryInterface to
              defend themselves appropriately.
              */

    HRESULT rc = S_OK;

    OdmWorking *pOdmWorking
        = new(std::nothrow)
            OdmWorking( (IUnknown *) pIodmApp,
                        pszDocId, pszDocLoc, viewMode  );

    if (pOdmWorking == NULL) return E_OUTOFMEMORY;

        /* We have an OdmWorking object with refCount = 0 and with
           nothing created that a simple delete can't handle at this point.
           */

    rc = pOdmWorking -> QueryInterface(rIID, ppIface);
            /* It is assumed that the IodmWorking object QueryInterface
               defends itself and will not crash. */

    if (rc != S_OK) delete pOdmWorking;
        /* If we can't produce the interface the customer wants,
           delete the OdmWorking object and pass on the bad news.

           If the interface is delivered, refCount = 1 now and it is
           up to the caller to release the interface and allow the
           implementing object to clean up and delete itself.
           (See the code for OdmWorking::Release.)
           */

    return rc;

    } /* OdmBindWorking100 */



/* 0.08 2007-01-19-12:41 Implement openPendingDocument for completing the
        0.50beta support of transferToNewDocument behavior.
   0.07 2007-01-19-11:07 Implement deriveNewDoc as the first piece of
        support for transferToNewDocument in ODMJNI 1.0 0.50beta.
   0.06 2007-01-12-15:37 Implement commitChanges() and provide any other
        touch-ups appropriate for use in ODMJNI 1.0 0.40alpha.
   0.05 2007-01-01-12:15 Correct names to supply missing OdmWorking methods.
   0.04 2006-12-31-18:42 Get clean compile and make any other touch-ups
        noticed while checking.  This OdmWorking100.obj is 7,149 bytes.
   0.03 2006-12-30-14:16 Adjust to use separate header for the factory
        function, making other corrections noticed in code review so far.
   0.02 2006-12-29-20:15 Cross-check with the header file and make sure
        that everything is consistent for ODMJNI 0.30alpha testing.
   0.01 2006-12-27-19:36 Repave this to use a better constructor for
        OdmWorking, avoiding problems about delayed refcounts and all of
        that silliness.  We solve this by starting out with a docLocation
        from the get-go.
   0.00 2006-12-23-17:34 Use OdmNative100 0.21 as a skeleton for morphing
        into an OdmWorking100 implementation.  Tidy up the identification
        and history.  Then fix the class declaration followed by
        creating the new methods, one by one.

   $Header: /ODMdev/info/odma/OdmNative100/OdmWorking100.cpp 10    07-01-19 13:07 Orcmid $
   */

/*                     *** end of OdmWorking100.cpp ***                  */

