/* OdmJniApp.java 0.15               UTF-8                  dh:2007-11-10
 *
 * OdmJniApp is a package class that delivers an OdmConnection inter-
 * face with an OdmNative class implementation.  The class inherits from
 * OdmNullConnection.
 *
 * The constructor accepts an interface reference for an IodmApplication100
 * COM interface that is used to coordinate with OdmNative components.  The
 * lifecycle of that interface reference must be managed by the OdmJniApp
 * instance.
 *
 * Instances are constructed by OdmJniBind.application when it is determined
 * that an IodmApplication100 interface is acquired.
 */

package info.odma.odmjni100;


class OdmJniApp extends info.odma.practical100.OdmNullConnection
                implements info.odma.practical100.OdmConnection
{
    /* This is a standard class that overloads OdmNullConnection methods
       to accomplish its functions.  Native methods used to coordinate
       with ODMA are implemented with info.odma.odmjni100.OdmJniBind static
       methods and with static methods of this class.
       */

    private long rIodmNative;
        /* This is the interface reference that is held on behalf of
           the OdmConnection implementation carried in OdmJniApp.
           */

    private boolean knowConnection;
        /* True once we know the connectionAvailable() response. */

    private boolean knownConnection;
        /* The known value, once one is known.  This can be over-ridden
           by release(), whether or not previously known, because
           release() sets this false and also clears rIodmNative.
           after freeing the interface, if any.
           */

    private boolean knowDefaultDMS;
        /* True once we know the dmsDefaultAvailable() response. */

    private boolean knownDefaultDMS;
        /* The known value, once it is known.  This can be over-ridden
           by release(), in the same manner as knownConnection.
           */


    private static native boolean checkViewOnly(long rIodmWorking);
        /* Provided an IodmWorking100 interface reference, this function
           returns the result of IodmWorking100.viewOnly() as a boolean.
           */

    private static native boolean hasConMan(long rIodmNative);
        /* This native function reports whether or not OdmNative100
           has linked the ODMA Connection Manager or not.
           */



    private static native boolean hasDefaultDMS(long rIodmNative);
        /* This native function reports whether or not OdmNative100
           has acquired a Default DMS for the application or not.
           */

    private static native long
            jniNewDoc(              long rIodmNative,
                        java.lang.String docFormatName );
        /* This native function will establish a new document using the
           native connection, if possible, and return a result code
           accordingly.
           XXX: This function is expected to fail safely if the
                docFormatName string is ill-formed.  This means that the
                documented format requirement for ODMJNI is the same as
                used by OdmNative100 functions.  We may have to smooth
                this somehow.
           */


    private static native long selectDoc(long rIodmNative);
        /* This native function will selectDoc if it can and return
           an rIodmWorking interface reference for a working document or
           else return a result code.

           The operation returns a NULL interface reference if
           a non-interface reference is provided as input.
           */


    private static native
        long jniKnownDocument(               long rIodmNative,
                                 java.lang.String  docId );
        /* This native function will provide similar results to
           selectDoc except that the docId is provided as an input
           parameter.  info.odma.practical100.OdmFormat.wfDocId(docId)
           must be true and the only defense required of jniKnownDoc
           is to assure translatability and satisfaction of the length
           constraint on ODMA Document IDs.

           XXX: We may have trouble with this assumption about having
                pre-screened the docId string.  This function must fail
                safely when given an ill-formed docId, and the rules must
                be the same as those of the new OdmFormatCheck solution.
           */



    protected OdmJniApp( long rIface  /* The interface to an IodmNative */
                         )

    {   /* Hold the interface for maintenance as long as other methods
           depend on its retentions.  The release procedure must make
           sure that it is released too.

           XXX: This class should be constructed with a valid rIface value.
                If the rIface is null or an error status, it will provide
                its null behavior, however.  The guard is implemented in
                OdmJniApp.available.
           */

        super();

        rIodmNative =
            info.odma.odmjni100.OdmJniBind.holdNativeInterface
                ( info.odma.odmjni100.OdmJniBind.getInterfaceRef(rIface) );
            /* so that rIodmNative is either 0 or a reference */

        knowConnection = false;
            /* We don't know yet and we'll figure it out when asked. */

        knownConnection = false;
            /* Safety-measure superstition */

        knowDefaultDMS = false;

        knownDefaultDMS = false;
            /* Ditto for dmsDefaultAvailable information */

            /* FIXME: There needs to be a way to pass in the OdmNullCache
                      being used.  This looks to be an alternative
                      constructor on OdmNullCache to allow customization
                      from actual connection and document implementations,
                      such as those here in odmjni100.  There must also
                      be allowance for reverting from the formatCheck to
                      the nullCheck of the OdmNullCache.

                      XXX: This implementation uses the implementation of
                           OdmFormatCheck of OdmNullCache.  There is no
                           difference between connected interfaces and
                           null implementations, but only for 0.59 beta.
                      */

        } /* OdmJniApp */



    /* OdmInterface Interface Method Implementations
     * ---------------------------------------------
     */

    public java.lang.String interfaceImplementation()
    {   /* Return a text string describing this implementation */

        return "ODMJNI 1.0 OdmJniApp 0.15";

        /* The default toString() identifies the object but does not
           provide anything apart from the class name of the implementation.
           This result is intended to be more useful in trouble-shooting
           and identification of implementation version in problem analysis.
           */

        } /* interfaceImplementation */



    public boolean available()
    {   /* True when ODMA is available for use.

           When the result is false, all operations provide their null
           behavior.

           This is similar to the implementation of the
           IodmNative100::hasConMan() method, except that release can
           change the known response.
           */

    if (!knowConnection)
        {   knowConnection = true;
                /* We are about to know, either way. */
            if (rIodmNative == 0)
                 knownConnection = false;
                    /* Because not possible without the interface */
            else knownConnection = hasConMan(rIodmNative);
                    /* defining having a connection as equivalent to
                       having the ODMA Connection Manager available.
                       */
            }

    return knownConnection;

    } /* available */



    public void release()
    {   /* If a native interface is being held, it must be released
           now. */

        if (rIodmNative != 0)
             info.odma.odmjni100.OdmJniBind.freeNativeInterface
                 (rIodmNative);
        rIodmNative = 0;

        knownConnection = false;
            /* and it now stays that way until finalize(). */

        knownDefaultDMS = false;
            /* And this stays that way until finalize() too. */

        /* Other release operations go in here. */

        super.release();

        } /* release */



    protected void finalize()
                   throws Throwable
    {   /* We must do this, in case this object becomes inaccessible
           without release() having been performed.  This is how
           we intend to clean up after ourselves no matter what.

           The throw clause is required by finalize() inheritance from
           java.lang.Object.  No checked exceptions arise from the
           implementation here.
           */

        release();

        super.finalize();

        } /* finalize */


        /* XXX: formatCheck is implemented by inheritance.  There is no
                separate implementation in the current odmjni100.  This
                will change to be connection-sensitive in 0.60 and beyond.
                */


     /* OdmConnection Interface Method Implementations
     * ----------------------------------------------
     */


    public boolean dmsDefaultAvailable()
    {   /* This method behaves similarly to available().
           The first time we are asked, we find out if that is
           the case.  We give the same answer thereafter, and
           that answer will be changed by release() since the
           IodmNative interface is released and the methods of
           this interface implementation instance have no default
           DMS available thereafter.

           This is similar to IodmApplication100::hadDefaultDMS.
           */

        if (!knowDefaultDMS)
        {   knowDefaultDMS = true;
                /* We are about to know, either way. */
            if (!available())
                 knownDefaultDMS = false;
                    /* Because not possible without the connection */
            else knownDefaultDMS = hasDefaultDMS(rIodmNative);
            }

        return knownDefaultDMS;

        } /* dmsDefaultAvailable */



    public info.odma.practical100.OdmPendingDocument
                acceptNewDocument(java.lang.String docFormatName)

    {   /* Return an OdmPendingDocument based on the result of the
           odmjni native newDoc() operation.

           The results are handled as follows:

                0   ODM_SUCCESS (but non-null Interface reference)
                    return an OdmJniPend result with that interface

                2   ODM_E_CANCEL
                    return nullDocument.pendingCancelled()

                6   ODM_E_APPSELECT
                    return nullDocument.pendingLocalOperation()

            other   same as ODM_E_FAIL (1)
                    return nullDocument.workingFailed()
           */

        if (!dmsDefaultAvailable())
             return super.acceptNewDocument(docFormatName);
                /* If we don't have a dms, then we are not going
                   anywhere but to the null response.
                   */

        long rIpend = jniNewDoc(rIodmNative, docFormatName);
        /* XXX: We are depending on jniNewDoc and the OdmNative100 to
                fail directly when docFormatName is ill-formed.
                */

        int iStatus =
                info.odma.odmjni100.OdmJniBind.getInterfaceStatus(rIpend);

        if (rIpend == 0 || iStatus > 0)

             switch (iStatus)
             {  case(2):
                     return nullDocument.pendingCancelled();
                case(6):
                     return nullDocument.pendingLocalOperation();
                default:
                     return nullDocument.pendingFailed();
                }

        /* We have a successful result.  Deliver it to the application
           as an OdmJniPend OdmPendingDocument implementation.
           */

        info.odma.practical100.OdmPendingDocument odmPending = null;

        try
        {   odmPending = new info.odma.odmjni100.OdmJniPend
                                   ( rIpend, nullDocument);
            }
        catch (java.lang.Throwable e) {  }

        info.odma.odmjni100.OdmJniBind.freeNativeInterface(rIpend);

        if (odmPending == null)
             return nullDocument.pendingFailed();

        return odmPending;

        } /* acceptNewDocument */



    public info.odma.practical100.OdmWorkingDocument
                chooseDocument()

    {   /* Return an OdmWorkingDocument based on the result of the
           odmjni native selectDoc() operation.

           The results are handled as follows:

                0   ODM_SUCCESS (but non-null Interface reference)
                    return an OdmJniWork result with that interface

                2   ODM_E_CANCEL
                    return nullDocument.workingCancelled()

                6   ODM_E_APPSELECT
                    return nullDocument.workingLocalOperation()

            other   same as ODM_E_FAIL (1)
                    return nullDocument.workingFailed()
           */

        if (!dmsDefaultAvailable())
             return super.chooseDocument();
                /* If we don't have a dms, then we are not going
                   anywhere but to the null response.
                   */

        long rIchoice = selectDoc(rIodmNative);

        int iStatus =
                info.odma.odmjni100.OdmJniBind.getInterfaceStatus(rIchoice);

        if (rIchoice == 0 || iStatus > 0)

             switch (iStatus)
             {  case(2):
                     return nullDocument.workingCancelled();
                case(6):
                     return nullDocument.workingLocalOperation();
                default:
                     return nullDocument.workingFailed();
                }

        /* We have a successful result.  Deliver it to the application
           as an OdmJniView or OdmJniWork OdmWorkingDocument implementation.
           */

        info.odma.practical100.OdmWorkingDocument rIwork = null;

        try
        {   if (checkViewOnly(rIchoice))
                 rIwork = new info.odma.odmjni100.OdmJniView
                                   ( rIchoice, nullDocument);
            else rIwork = new info.odma.odmjni100.OdmJniWork
                                   ( rIchoice, nullDocument);
              /* delivering modifiable or read-only implementations
                 as required by the delivered choice.
                 */
            }
        catch (java.lang.Throwable e) { rIwork = null; }

        info.odma.odmjni100.OdmJniBind.freeNativeInterface(rIchoice);

        if (rIwork == null)
             return nullDocument.workingFailed();

        return rIwork;

        } /* chooseDocument */




    public info.odma.practical100.OdmWorkingDocument
                openKnownDocument(java.lang.String docId)

    {   /* Return an OdmWorkingDocument based for on the result of the
           odmjni native jniKnownDocument operation.

           The results are handled as follows:

                0   ODM_SUCCESS (but non-null Interface reference)
                    return an OdmJniView result with that interface

                1   ODM_E_FAIL
                    return nullDocument.workingFailed()

            other   same as ODM_E_FAIL (1)
                    return nullDocument.workingFailed()
           */

        if (!dmsDefaultAvailable())
             return super.openKnownDocument(docId);
                /* If we don't have a dms, then we are not going
                   anywhere but to the null response.
                   */

        if (!formatCheck().wfDocId(docId))
             return nullDocument.workingFailed();
                /* XXX: This is required so so that the only defense that
                        jniKnownDocument must provide is against
                        translation and length. It is useful to be able
                        to rely on the OdmNative100 defenses for this,
                        and that can happen out of the refactoring.  We
                        won't remove this until we have confirmed that to
                        be the case at some future point.

                    FIXME: Change to be able to rely on jniKnownDocument
                           after confirming that it fully defends itself.
                        */

        long rIchoice = jniKnownDocument(rIodmNative, docId);
        /* FIXME: The code below here could be implemented in common
                  with chooseDocument() where the only difference is the
                  rIchoice provided as an input parameter.
                  */

        int iStatus =
                info.odma.odmjni100.OdmJniBind.getInterfaceStatus(rIchoice);

        if (rIchoice == 0 || iStatus > 0)
             return nullDocument.workingFailed();


        /* We have a successful result.  Deliver it to the application
           as an OdmJniView or OdmJniWork OdmWorkingDocument implementation.
           */

        info.odma.practical100.OdmWorkingDocument rIwork = null;

        try
        {   if (checkViewOnly(rIchoice))
                 rIwork = new info.odma.odmjni100.OdmJniView
                                   ( rIchoice, nullDocument);
            else rIwork = new info.odma.odmjni100.OdmJniWork
                                   ( rIchoice, nullDocument);
              /* delivering modifiable or read-only implementations
                 as required by the delivered choice.
                 XXX: This works because OdmJniView also implements an
                      OdmWorkingDocument interface, with the proper
                      failing methods.
                 */
            }
        catch (java.lang.Throwable e) { rIwork = null; }

        info.odma.odmjni100.OdmJniBind.freeNativeInterface(rIchoice);

        if (rIwork == null)
             return nullDocument.workingFailed();

        return rIwork;

        } /* openKnownDocument */




    static

        {   /* Static initialization of this class specifies the
               loading of the only DLL that we'll use. */

            System.loadLibrary("odmjni100");

            } /* static initialization */


    } /* OdmJniApp */


/* 0.15 2007-11-10-16:19 Implement formatCheck by inheritance from OdmNull-
        Connection.  Change openKnownDocument to use it to check the docId.
   0.14 2007-10-30-20:15 Tighten the commentary around the simplified
        format checking in the constructor, acceptNewDocument, and
        openKnownDocument.
   0.13 2007-10-17-17:11 Remove all exceptions for format errors and
        produce failed/null implementations for results either directly
        or as a consequence of OdmNative100 behavior.
   0.12 2007-03-07-16:21 Add openKnownDocument implementation that relies
        on OdmFormat.wfDocId of version 0.09 (0.56beta) to leave only
        translatability and length for verification in the jniKnown-
        Document native implementation.  Add parameter format checking
        to acceptNewDocument.  Correct some out-of-date comments.
   0.11 2007-01-25-15:23 Correct leaking interface in acceptNewDoc()
   0.10 2007-01-20-17:28 Add acceptNewDocument implementation, following
        the pattern of chooseDocument which is of exactly the same pattern.
   0.09 2007-01-15-14:53 Add hasConMan, hasDefaultDMS, and selectDoc as
        private native methods that are used exclusively by OdmJniApp for
        implementation of the OdmConnection interface to ODMJNI 1.0.
   0.08 2007-01-12-14:37 Add the checkViewOnly private static native method.
        Use odmjni100.dll as the implementation DLL.  This is the first
        step of refactoring the declaration of native methods onto the
        classes that they should be private to.  chooseDocument is updated
        to return viewable-only or modifiable document content as needed.
   0.07 2007-01-07-21:30 Implement full chooseDocument functionality, with
        additional touch-ups noticed since 0.25alpha. As the native
        selectDoc function is improved, the difference will automatically
        show up here.  This chooseDocument always produces a viewOnly
        Working Document until we have OdmJniWrk in 0.40alpha.
   0.06 2006-12-18-21:29 Adjust to use of OdmNullCache in NullDocument
        and used here by inheritance.  This class is 1,751 bytes and it
        doesn't appear that size is going to be an issue.
   0.05 2006-12-18-13:34 Review and adjust for operation with the 0.30alpha
        versions of practical100 and use in the 0.25alpha regression use of
        odmjni100.
   0.04 2006-11-29-23:45 Add skeletal chooseDocument() enough to get
        selects to be performed but we only return null working documents.
   0.03 2006-11-29-21:37 Implement the dmsDefaultAvailable() behavior.
   0.02 2006-11-29-19:44 Implement the connectionAvailable() behavior.
   0.01 2006-11-29-17:19 Hold the supplied interface and manage its
        life-cycle.
   0.00 2006-11-29-12:20 Introduce nearly-null case that confirms the
        correct employment of OdmJniApp objects when OdmNative is bound
        by OdmJniBind.application.  The delivered interface reference is
        not used, so it will have been released by OdmJniBind.application.

   $Header: /ODMdev/info/odma/odmjni100/OdmJniApp.java 16    07-11-10 16:25 Orcmid $
   */

/*              *** end of info.odma.odmjni100.OdmJniApp.java ***         */