/* odmjni100.cpp 0.12                UTF-8                   dh:2007-01-24
 *
 *
 *                 IMPLEMENT THE NATIVE METHODS OF ODMJNI100
 *                 *****************************************
 *
 * This is the C++ Language implementation of the native-method DLL
 * for odmjni100.  It is developed progressively in line with the
 * expansion of info.odma.odmjni100 OdmJniBind class support for
 * different ODMA operations.
 *
 * We have native methods for several OdmJni classes, including OdmJniBind
 * and OdmJniVew, but we will still have a single DLL.  This maximizes
 * on the statically-bound C++ DLL runtime for the native methods and the
 * OdmNative libraries.  We also reduce the amount of DLL loading.
 */


#include <jni.h>
    /* For the standard JNI interface.  This interface will be the
       C++ version when building odmjni100.dll.
       */

#include "Odma32types100.h"
    /* For ODMA-specific types and values that we rely on and produce. */

#include "odmjni100x.h"
    /* Rely on the annotated cumulatively-updated header. */

#include <windows.h>
    /* For native interfaces and COM-related definitions. */

#include "OdmNative100.hpp"
    /* For functions and classes of OdmNative and IodmNative. */

#include "OdmWorking100.hpp"
    /* For the IID and the interface pointer, basically, as well as
       usages adopted from OdmNative100 test\Check05 and Check06 */

#include "OdmPending100.hpp"
    /* For the IID and the interface pointer here too, as well as
       usages adopted from OdmNative 100 test\Check06 */





    /* IMPLEMENTATION OF ODMJNIBIND NATIVE METHODS
       ******************************************* */


JNIEXPORT
    jint JNICALL
         Java_info_odma_odmjni100_OdmJniBind_jniVersion
             (  JNIEnv *pIenv,       /* JNI environment interface */
                jclass pThisClass    /* not used */
                )

{   /* Return the JNI Version Number m.n, coded as 0x000m000n.
       pIenv is a ppv. That is, JNIEnv is a pv, and what's passed
       in is a ppv.  In C++, Ienv is a class with interface methods.
       */

    return pIenv->GetVersion();

    } /* jniVersion */



static const IID IID_IodmApplication100 = IID_IodmApplication100_ ;
    /* IodmApplication100 IID of the current header level. */

static const IID IID_IodmWorking100 = IID_IodmWorking100_ ;
    /* IodmWorking100 IID of the current header definition/contract */

static const IID IID_IodmPending100 = IID_IodmPending100_ ;



                    /* ODMJNIBIND STATIC NATIVE METHODS *
                     * ******************************** *
                     */

JNIEXPORT
    jlong JNICALL
        Java_info_odma_odmjni100_OdmJniBind_getIodmNative
            (   JNIEnv *pIenv,      /* JNI environment interface */
                jclass pThisClass,  /* not used */
               jstring jAppId       /* The Application ID */
               )

{   /* This procedure binds an IodmApplication100 interface using
       the OdmBindNative100 function.
       */

    const char *pszAppId
        = pIenv -> GetStringUTFChars(jAppId, NULL);
            /* Get pointer to the UTF (presumably-ASCII string.
               XXX: We let OdmBindNative100 check the string for
               us.  It should have been checked by OdmJniBind before
               we got to here. */

    IUnknown *pIUnk = NULL;
            /* receptacle for the pIodmNative returned from
               OdmBindNative100.  We ask for IodmApplication100
               here so that other code doesn't have to query
               interface and we have a check on the proper version
               by IID match-up.
               */

    HRESULT rc = OdmBindNative100(  pszAppId,
                                    IID_IodmApplication100,
                                    (void **) &pIUnk
                                    );
            /* If everything is working, we get an interface that is
               reserved for us (and hence our caller). */

    pIenv -> ReleaseStringUTFChars(jAppId, pszAppId);
            /* Let go of the AppID UTF - we don't need it any longer. */

    if (SUCCEEDED(rc)) return (jlong) pIUnk;
            /* Hand out the disguised pIUnk that we succeeded in getting.*/

    return (jlong) ODM_E_FAIL;
            /* Anything else produces a low-numbered value. */

    } /* getIodmNative */



JNIEXPORT
    jlong JNICALL
        Java_info_odma_odmjni100_OdmJniBind_holdNativeInterface
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* A disguised Native pointer */
                )
{   /* This is a standard IUnknown::AddRef() operation.  It could
       also be a QueryInterface as a way of also normalizing the
       interface, but we will go for speed instead. */

    if (rIodmNative == (rIodmNative & 0x1F))
         return 0;
            /* XXX: Simply ignore non-pointer values. */

    ((IUnknown *) rIodmNative) -> AddRef();

    return rIodmNative;

    } /* holdNativeInterface */



JNIEXPORT
    void JNICALL
        Java_info_odma_odmjni100_OdmJniBind_freeNativeInterface
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* a disguised Native pointer */
                )
{   /* Perform a standard IUnknown::Release() using the disguised
       interface reference we are handed.
       */

    if (rIodmNative == (rIodmNative & 0x1F)) return;
            /* XXX: Simply ignore non-pointer values. */

    ((IUnknown *) rIodmNative) -> Release();

    } /* freeNativeInterface */




                    /* ODMJNIAPP STATIC NATIVE METHODS *
                     * ******************************* *
                     *
                     * This method is private to OdmJniApp
                     * and others should be here too.
                     */


JNIEXPORT
    jboolean JNICALL
        Java_info_odma_odmjni100_OdmJniApp_checkViewOnly
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* An IodmWorking100 reference */
                )

{   /* It's available directly in the IodmWorking100 interface.
       */

    return ((IodmWorking100 *) rIodmNative) -> viewOnly();

    } /* checkViewOnly */



JNIEXPORT
    jboolean JNICALL
        Java_info_odma_odmjni100_OdmJniApp_hasConMan
            (   JNIEnv  *pIenv,     /* not used */
                jclass  pThisClass, /* not used */
                 jlong  rIodmNative /* a disguised Native pointer */
                )
{   /* Perform IodmApplication100::hasConMan() and that's all
       we need. */

    /* XXX: Technically, we should do a query interface here, but we
       assert that we have IodmApplication100 every time this
       method is called.
       */

    return ((IodmApplication100 *) rIodmNative) -> hasConMan();


    } /* hasConMan */



JNIEXPORT
    jboolean JNICALL
        Java_info_odma_odmjni100_OdmJniApp_hasDefaultDMS
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* a disguised Native pointer */
                )
{   /* Perform IodmApplication100::hasDefaultDMS() and we're done.
       */

    return ((IodmApplication100 *) rIodmNative) -> hasDefaultDMS();

    } /* hasDefaultDMS */


JNIEXPORT
    jlong JNICALL
        Java_info_odma_odmjni100_OdmJniApp_jniNewDoc
            (   JNIEnv *pIenv,          /* used to convert the string */
                jclass pThisClass,      /* not used */
                 jlong rIodmNative,     /* disguised pIodmApplication100 */
               jstring jDocFormatName   /* validated Document Format Name */
               )

{   /* Perform everything needed to get to an IodmPending for a new
       document or else a result code that provides for alternative
       action by the OdmJniApp acceptNewDocument method.

       This implementation follows the lines of the implementation
       in OdmNative100\test\Check06\CheckNew.cpp.  That provides the
       essential functionality.

       XXX: This implementation is not called if rIodmNative is NULL (0).
            The DocFormatName is expected to be prevalidated and no
            invalid values are delivered to this implementation.
       */

    IodmApplication100 *pIodmApp = (IodmApplication100 *) rIodmNative;

    const char *pszDocFormat
        = pIenv -> GetStringUTFChars(jDocFormatName, NULL);
            /* We must be careful to give this back before any exit
               from this implementation */

    char theDocId[ODM_DOCID_MAX+2] = {'\0'};
            /* Buffer for any returned Document ID, with a little
               extra padding.
               */

    ODMSTATUS odmResult = pIodmApp -> startNewDoc(theDocId, pszDocFormat);

    pIenv -> ReleaseStringUTFChars(jDocFormatName, pszDocFormat);
            /* We have no use below here. */

    if (odmResult != ODM_SUCCESS)
         return (jlong) odmResult;
                /* There's nothing more to do if there's no Document ID */

    char theDocLoc[ODM_FILENAME_MAX+20] = {'\0'};
        /* Where we store our version of the Document Location */


    odmResult = pIodmApp -> openDoc(theDocId, theDocLoc, FALSE);
                /* The ID was from a startNewDoc, so this should work. */

    if (odmResult != ODM_SUCCESS)
         return (jlong) odmResult;

    IodmPending100 *pIodmPend = NULL;

    HRESULT rc = pIodmApp -> makePendingDocument
                                    (  theDocId, theDocLoc,
                                       IID_IodmPending100,
                                       (void**) &pIodmPend
                                       );

    if (SUCCEEDED(rc))
         return (jlong) pIodmPend;

    return (jlong) ODM_E_FAIL;


    } /* jniNewDoc */



JNIEXPORT
    jlong JNICALL
        Java_info_odma_odmjni100_OdmJniApp_selectDoc
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* a disguised Native pointer */
                )
{   /* Use IodmApplication100::selectDocID to connect to the DMS and
       return the selection from the user.

       We return either an ODMSTATUS value or we return an
       IodmWorking100 interface reference.

       This implementation is based on the OdmNative100 test Check05 0.07
       approach.  When OdmNative100 is refactored to to use cohesive
       methods, this function will become much simpler.
       */

    if (rIodmNative == (rIodmNative & 0x1F)) return 0;
            /* XXX: Simply ignore non-pointer values. */

    IodmApplication100 *pIodmApp = (IodmApplication100 *) rIodmNative;
            /* Use the interface pointer we are given.
               XXX: We do not QueryInterface because we trust the caller
                    to provide the correct goodies.  This function should
                    really be a native method of the OdmJniApp class.
               */

    char theDocId[ODM_DOCID_MAX+2]={'\0'};
            /* FIXME: We need serious buffer over-run
                      protection here, although when we refactor this will
                      all be handled by the IodmAppication100
                      implementation..
               */

    BOOL viewMode = TRUE;
            /* FIXME: We need a better approach to this when we allow
               other cases.  It may be trickier to accomplish this
               when the DMS may push back on what we want.
               */

    ODMSTATUS rc = pIodmApp -> selectDocID(theDocId, &viewMode);

    if (rc != ODM_SUCCESS)
         switch (rc)
         {  /* Stop now with the failure case we have */

            case      ODM_E_FAIL:
            case    ODM_E_CANCEL:
            case ODM_E_APPSELECT:
                        return (jlong) rc;
            default:    return (jlong) ODM_E_FAIL;
            }

    /* We have a good Document ID.  Now we need to see if we can
       open the document.
       */

    char theDocLoc[ODM_FILENAME_MAX+20] = {'\0'};

    rc = pIodmApp -> openDoc(theDocId, theDocLoc, viewMode);
            /* The select succeeded, so the open should work */

    if (rc != ODM_SUCCESS)
         return (jlong) ODM_E_FAIL;

    IodmWorking100 *pIodmWrk = NULL;

    HRESULT hr = pIodmApp -> makeWorkingDocument
                                (  theDocId, theDocLoc, viewMode,
                                   IID_IodmWorking100,
                                   (void**) &pIodmWrk
                                   );

    if (SUCCEEDED(hr))
         return (jlong) pIodmWrk;
    else return (jlong) ODM_E_FAIL;


    } /* selectDoc */



                    /* ODMJNIPEND STATIC NATIVE METHODS *
                     * ******************************** *
                     *
                     * XXX: These methods are private to
                     *      OdmJniPend, the only class
                     *      that relies on them.
                     */


JNIEXPORT
    jstring JNICALL
        Java_info_odma_odmjni100_OdmJniPend_jniPendLoc
            (   JNIEnv *pIenv,      /* Used for string conversion */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* rIodmPending100 */
                )
{   /* Deliver the document location for delivering initial content
       for the pending document.  This is modelled on the OdmJniView
       jniDocLoc method.
       XXX: The OdmJniPend.docSubmissionLocation method will not call
            this implementation when rIodmNative is NULL (0).
       */

    return pIenv -> NewStringUTF
                        (  ((IodmPending100 *) rIodmNative)
                                -> docLocation()
                           );

    } /* jniPendLoc */



JNIEXPORT
    jlong JNICALL
        Java_info_odma_odmjni100_OdmJniPend_jniCommitContent
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* rIodmPending100 */
                )

{   /* Implement OdmJniPend.commitContent()'s request of an IodmWorking100
       interface for the committed content.  Any return of a non-reference
       is treated as an ODM_E_FAIL response.
       XXX: The OdmJniPend.commitContent method will not call this
            implementation when rIodmNative is NULL (0).
       */

    IUnknown *pIUnk = NULL;
        /* The provisional pointer */

    IodmWorking100 *pIwork = NULL;

    HRESULT rstatus
                = ((IodmPending100 *) rIodmNative) -> commitContent(&pIUnk);

    if (!SUCCEEDED(rstatus))
         return (jlong) ODM_E_FAIL;

    rstatus = pIUnk -> QueryInterface(  IID_IodmWorking100,
                                        (void **) &pIwork
                                        );

    if (!SUCCEEDED(rstatus))
         return (jlong) ODM_E_FAIL;

    pIUnk -> Release();

    return (jlong) pIwork;


    } /* jniCommitContent */



                    /* ODMJNIVIEW STATIC NATIVE METHODS *
                     * ******************************** *
                     *
                     * XXX: These methods are private to
                     *      OdmJniView, the only class
                     *      that relies on them.
                     */

JNIEXPORT
    jstring JNICALL
        Java_info_odma_odmjni100_OdmJniView_jniDocID
            (   JNIEnv *pIenv,      /* Used to make string */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* an IodmWorking100 reference */
                )

{   /* Take the IodmWorking100::docID() right through to conversion
       to Java Unicode.
       */

    return pIenv -> NewStringUTF
                        (  ((IodmWorking100 *) rIodmNative)
                                -> docID()
                           );

    /* XXX: This approach depends on the ODMA Document ID being limited
            to ISO 646/ASCII Character codes that are one-to-one
            equivalent to Unicode code points.
       */

    } /* jniDocID */




JNIEXPORT
    jstring JNICALL
        Java_info_odma_odmjni100_OdmJniView_jniDocLoc
            (   JNIEnv *pIenv,      /* Used to make string */
                jclass pThisClass,  /* Not used */
                 jlong rIodmNative  /* an IodmWorking100 reference */
                 )

{   /* Take the IodmWorking100::docLocation() right through to conversion
       to Java Unicode.
       */

    return pIenv -> NewStringUTF
                        (  ((IodmWorking100 *) rIodmNative)
                                -> docLocation()
                           );

    /* XXX: This approach depends on the ODMA Document Location being
            limited to ISO 646/ASCII Character codes that are one-to-one
            equivalent to Unicode code points.
       */

    } /* jniDocLoc */


JNIEXPORT
    jstring JNICALL
        Java_info_odma_odmjni100_OdmJniView_jniDocProp
            (   JNIEnv *pIenv,      /* Used to make string */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative, /* an IodmWorking100 reference */
                  jint propNum      /* item number of desired property */
                )

{   /* Use OdmWorking100::docProperty to obtain the item, if available. */

    char thePropVal[300] = {'\0'};
            /* Provide generous storage for the string */

    ODMSTATUS rc = ((IodmWorking100 *) rIodmNative)
                    -> docProperty(propNum, thePropVal, sizeof(thePropVal));

    if (rc != ODM_SUCCESS) return (jstring) NULL;

    return pIenv -> NewStringUTF(thePropVal);

    } /* jniDocProp */



JNIEXPORT
    jlong JNICALL
        Java_info_odma_odmjni100_OdmJniView_jniToNewDoc
            (   JNIEnv *pIenv,      /* Used to convert the string */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative, /* an IodmWorking100 reference */
               jstring jDocFormat   /* format desired for the new document
                                       */
               )
{   /* Use the IodmWoring100 methods for deriveNewDoc followed by
       an openPendingDoc to bring forth a ready-for-content Pending
       Document.  This implementation logic follows the implementation
       of acceptNewDocument vary closely, except for choice of methods.
       XXX: This function is not called without a valid rIodmNative
            and a validated jDocFormat string.
       FIXME: We are skating around the second precondition and
              that must be repaired in OdmJniView ASAP.
       */

    IodmWorking100 *pIodmWork = (IodmWorking100 *) rIodmNative;

    const char *pszDocFormat
        = pIenv -> GetStringUTFChars(jDocFormat, NULL);
            /* We must be careful to give this back before any exit
               from this implementation */

    char theDocId[ODM_DOCID_MAX+2] = {'\0'};
            /* Buffer for any returned Document ID, with a little
               extra padding.
               */

    ODMSTATUS odmResult = pIodmWork -> deriveNewDoc(pszDocFormat,
                                                    theDocId );

    pIenv -> ReleaseStringUTFChars(jDocFormat, pszDocFormat);
            /* We have no use below here. */

    if (odmResult != ODM_SUCCESS)
         return (jlong) odmResult;
                /* There's nothing more to do if there's no Document ID */

    IodmPending100 *pIodmPend = NULL;

    HRESULT rc = pIodmWork -> openPendingDoc
                                    (  theDocId,
                                       IID_IodmPending100,
                                       (void**) &pIodmPend
                                       );

    if (SUCCEEDED(rc))
         return (jlong) pIodmPend;

    return (jlong) ODM_E_FAIL;


    } /* jniToNewDoc */



                    /* ODMJNIWORK STATIC NATIVE METHODS *
                     * ******************************** *
                     *
                     * XXX: This method is private to
                     *      OdmJniWork, the only class
                     *      that relies on it.
                     */


JNIEXPORT
    jboolean JNICALL
        Java_info_odma_odmjni100_OdmJniWork_jniCommitChanges
            (   JNIEnv *pIenv,      /* not used */
                jclass pThisClass,  /* not used */
                 jlong rIodmNative  /* for an IodmWorking100 */
                )

{   /* It's available directly in the IodmWorking100 interface.
       */

    return ((IodmWorking100 *) rIodmNative) -> commitChanges();

    } /* commitChanges */



/* 0.12 2007-01-24-22:16 Correct IID bug in jniCommitContent QueryInterface
   0.11 2007-01-22-22:33 Implement the native method jniToNewDoc and
        obtain clean compile.
   0.10 2007-01-21-20:40 Implement the native methods jniNewDoc, jniPendLoc,
        and jniCommitContent.
   0.09 2007-01-15-16:28 Move hasConMan, hasDefaultDMS, and selectDoc to
        OdmJniApp.
   0.08 2007-01-13-18:27 addition of checkViewOnly() and commitChanges()
        for completion of all methods needed for the 0.40alpha release.
   0.07 2007-01-09-20:52 Introduce the three native methods used by
        OdmJniView 0.04 for the 0.30alpha release.  The DLL is still 68k.
   0.06 2007-01-08-21:44 Update with complete selectDoc implementation.  The
        solutions is a variant on that used in OdmNative100 Check05 0.07.
   0.05 2006-11-30-00:42 Add selectDoc implementation.  This is provisional
        for now because we don't have any OdmNative document objects to
        instantiate yet.  This DLL is 68k.  We don't want multiple DLLs so
        we will have this one be enough.
   0.04 2006-11-29-21:56 Add hadDefaultDMS implementation.
   0.03 2006-11-29-20:58 Add hasConMan implementation.
   0.02 2006-11-29-19:00 Add holdNativeInterface implementation.
   0.01 2006-11-29-13:10 Add getIodmNative and freeNativeInterface.  These
        provide enough to instantiate an OdmConnection implementation.  Now
        there are 69,632 bytes in the DLL.
   0.00 2006-11-28-17:04 Strip down from odmjni100 0.06 to include only
        the jniVersion() method for initial confirmation of DLL location.
        The DLL compiles to the magical 49,152 bytes.

   $Header: /ODMdev/info/odma/odmjni100/odmjni100.cpp 14    07-01-24 22:33 Orcmid $
   */

/*                         *** end of odmjni100.cpp ***                  */
