/*BEGINPROLOGUE****************************************************************
 * @copyright(disclaimer)                                                     *
 *                                                                            *
 * DISCLAIMER OF WARRANTIES.                                                  *
 *                                                                            *
 * The following IBM Content Manager Enterprise Edition code is sample code   *
 * created by IBM Corporation. IBM grants you a nonexclusive copyright        *
 * license to use this sample code example to generate similar function       *
 * tailored to your own specific needs. This sample code is not part of any   *
 * standard IBM product and is provided to you solely for the purpose of      *
 * assisting you in the development of your applications. This example has    *
 * not been thoroughly tested under all conditions. IBM, therefore cannot     *
 * guarantee nor may you imply reliability, serviceability, or function of    *
 * these programs. The code is provided "AS IS", without warranty of any      *
 * kind. IBM shall not be liable for any damages arising out of your or any   *
 * other parties use of the sample code, even if IBM has been advised of the  *
 * possibility of such damages. If you do not agree with these terms, do not  *
 * use the sample code.                                                       *
 *                                                                            *
 * Licensed Materials - Property of IBM                                       *
 * 5724-B19, 5697-H60                                                         *
 * � Copyright IBM Corp. 1994, 2013 All Rights Reserved.                      *
 *                                                                            *
 * US Government Users Restricted Rights - Use, duplication or disclosure     *
 * restricted by GSA ADP Schedule Contract with IBM Corp.                     *
 *                                                                            *
 * @endCopyright                                                              *
 ******************************************************************************

Prerequisite Informaiton:
    Doc Model Items are an extension of non-resource items.  Doc Model Parts
    are resource items.  Therefore, both the standar item & resource item
    samples are pre-requisit knowledge.  Please review the SItem... samples
    and the SResourceItem... samples.

Document Model - Documents Modeled by Parts
    DB2 Content Manager V8 provides a multi-part document model instead of 
    using standard non-resource items and single-binary resource items.  
    The built-in document model is the same as the single data model in
    V7.1.  Note that V8 introduces single-binary resource items that
    are like parts that stand on their own as independent items.

    The document model provided structures documents as an Item made up of
    parts.  A document within the DB2 Content Manager Document Model is an
    item that that extends the full functionality of the non-resource items to
    include parts.  Parts are just like resource items with nearly the full
    capability of resource items except that they are owned and controlled by
    their parent document rather than separate items.

    Compared to a resource item, a document separates the binary resource
    content from the root component of the document into separate part
    entities and therefore can support more than one binary object per document.
    However, there is some overhead to this level of separation.  If you
    have only one part, you can push the resource content into the root
    component of the document by storing it as a resource item.  Note that
    resource items perform faster than a single-part document.  However,
    there are many great cases where documents with the capability to store
    multiple parts make sense and perform well depending on your needs.
    
    An item can support parts if the item belongs to an item type that is
    defined with classification "document" (document model).  The item type
    also specifies which part types are supported by each "document" classified
    item type.  A document can contain any number of parts for each part type
    selected.  See SItemTypeCreationICM sample for more information on the
    definition.
    
    Typically most applications use the system-defined part types.  However
    you can create your own part types by creating item type definitions
    classified as "part" (document part).
    
    The part item type (identified by the PID's object type within a part DDO)
    is the validated true identify of a document part.  You can set the semantic
    type to any value of your choice.  However, there exists semantic type values
    for each of the system-defined part types.  When using system-defined part type,
    it is recommended that you also specify an equivalent semantic type.  When
    using user-defined part types, you might want to set the semantic type to the
    nearest similar system-defined part type value that it resembles.  However, you 
    can set the semantic type to any value of your choice if you do not want to give
    a clue to other applications that might not understand your user-defined part type.
    Therefore, you should not rely on the semantic type as the primary means to
    identify part types.
    
    The pre-defined semantic types are only alleged to most nearly resemble the part
    type they indicate.  Be careful of relying on semantic type values if your
    application reads items created by other applications or tools since the meaning
    and enforcement can vary among applications.  For example, do not rely on the 
    various part semantic types since parts are allowed to be set with any semantic
    type value.  Instead rely first on the PID's object type which identifies the
    exact item type which you might recognize or you can retrieve the definition to
    tell you more, such as classification as a part, XDO class, etc.  You can rely
    on the semantic type as a secondary measure if you do not recognize the object
    type.
    
    For example, consider semantic type DK_ICM_SEMANTIC_TYPE_BASE.
    This value does *not* uniquely identify or make it a BASE part.  The semantic type
    value is not validated or enforced.  The item type of the part (identified in the
    PID's object type for the part DDO) is what makes the part a BASE part.  BASE parts
    can be created with any semantic type of your choice.  Do set this semantic type
    for BASE parts, but when detecting part types, use the PID's object type first.
    Use this value if you created your own custom part item types so that other
    applications that might not know your custom part type and could map it to the
    nearest similar part type that it understands.

    System Defined Part Types
        There are a number of system-defined part types which must be
        selected for use with the item type at design time.  Some or all of
        the part types can be selected.  

      Part Name       Suggested Semantic Type       Description
    -------------  -------------------------------  -----------------------------------------------
    ICMANNOTATION  DK_ICM_SEMANTIC_TYPE_ANNOTATION  LOB Resource that holds an annotation.
    ICMBASE        DK_ICM_SEMANTIC_TYPE_BASE        LOB Resource that holds part of the doc.
    ICMBASETEXT    DK_ICM_SEMANTIC_TYPE_BASE        Text Resource that holds part of the doc.
    ICMBASESTREAM  DK_ICM_SEMANTIC_TYPE_BASE        Stream Resource that holds part of the doc.
    ICMNOTELOG     DK_ICM_SEMANTIC_TYPE_NOTE        Text Resource for any use.  Ex: Change History.
    -----------------------------------------------------------------------------------------------
        Note:  Constants listed are defined in DKConstantICM.h
               Part names are specified using string values containing the name listed.
        Note:  Remember the semantic types listed are suggestions and are not validated
               or enforced.  You should not expect these semantic types to always be set
               by other applications or tools.

    Adding / Modifying / Removing Parts
        Parts may be added or removed by simply adding or removing them from
        the Parts collection in the document.  Parts are modified by modifying
        them in the document item.  Do not call add(), update(), or del()
        directly on the Parts.  The document will be responsible for managing
        the parts.
        
    Retrieving Parts
        If you already have parts DDOs, either through an existing document
        in memory or returned by query, you can retrieve parts directly the
        same way you retrieve resource items and can be directly retrieved if
        you already have the DDOs.  However, if you want to start with a
        document and retrieve its parts additional parts listing options
        are available. 
    
        Parts are retrieved through the same retrieve interfaces shown in
        SItemRetrievalICM.  See SItemRetrievalICM for important information about
        retrieve options in general.  Through the same interfaces that you can
        request attributes, children, and links, you can also request
        parts to be listed by making additional selections using a
        DKRetrieveOptionsICM instance.  Additionally, you can also request
        attributes and resource content for parts in the same call.  Note that
        resource content is restricted to single-item retrieve only for either
        a single document with parts or individually retrieved parts as explained
        in SResourceItemRetrievalICM.
    
        An example of some of the parts-related retrieve options are listed below.
          - DKRetrieveOptionsICM::partsList(boolean)  
          - DKRetrieveOptionsICM::partsAttributes(boolean)  
          - DKRetrieveOptionsICM::partsPropertyAclName(boolean)  
          - DKRetrieveOptionsICM::resourceContent(boolean)  

          * See DKRetrieveOptionsICM for full list of parts-related retrieve
            options identified by names starting with parts____().
        
          * Each option is explained in detailed reference documentation (Javadoc)
            in DKRetrieveOptionsICM, including detailed explanations, performance
            considerations, and more for each option.
        
*******************************************************************************/

// Includes / Imports
#include <DKConstant.h>
#include <DKConstantICM.h>
#include <DKDatastoreICM.hpp>
#include <DKParts.hpp>
#include <DKLobICM.hpp>
#include <DKRetrieveOptionsICM.hpp>
#include <DKTextICM.hpp>
#include "SConnectDisconnectICM.hpp"
#include "SDocModelItemICM.hpp"


/************************************************************************************************
 *          FILENAME: SDocModelItemICM.java
 *                    ---------------------------------------------------------------------------
 *       DESCRIPTION: Creating / Updating / Deleting Document Model Items, Creating Parts,
 *                    Accessing the Parts Collection, Adding Parts to a Document, Removing Parts
 *                    from a Document, Deleting Parts
 *                    ---------------------------------------------------------------------------
 *     DEMONSTRATION: Creating / Updating / Deleting Document Model Items
 *                    Creating Parts
 *                    Accessing the Parts Collection
 *                    Adding Parts to a Document 
 *                    Removing Parts from a Document
 *                    Deleting Parts
 *                    List Document Parts
 *                    ---------------------------------------------------------------------------
 * COMMANDLINE USAGE: java SDocModelItemICM <database> <userName> <password>
 *                    ---------------------------------------------------------------------------
 *     PREREQUISITES: The Data Model must be defined.  If needed please run the following Samples  
 *                        - SAttributeDefinitionCreationICM
 *                        - SAttributeGroupDefCreationICM
 *                        - SReferenceAttrDefCreationICM
 *                        - SItemTypeCreationICM
 *                    The Item Types must be set to use the specified default Resource Manager.
 *                    Plese run the following samples.
 *                        - SResourceMgrDefSetDefaultICM
 *                        - SSMSCollectionDefSetDefaultICM
 *                    NOTE:  Sample SSampleModelBuildICM pulls together all of the above.  It 
 *                           must be edited first for the RM Confiuration information.
 *                    ---------------------------------------------------------------------------
 *    FILES REQUIRED: SConnectDisconnectICM.hpp/cpp
 *                    SDocModelItemICM_launch.cpp
 *                    SDocModelItemICM.hpp
 *                    SResourceItemICM_Document1.doc
 *                    SResourceItemICM_Text1.txt
 *                    SResourceItemICM_Text2.txt
 ************************************************************************************************/

    //-------------------------------------------------------------
    // Main
    //-------------------------------------------------------------
    /**
     * Run the Sample.
     * @param argc   Argument Count / Number of Arguments Specified
     * @param argv[] String Array containing arguments.  Optional arguments are <databse> <userName> <password>.
     * @return  Returns SConnectDisconnectICM_RC_SAMPLE_PASSED (0) if no sample program completed, SConnectDisconnectICM_RC_SAMPLE_FAILED (-1) if any errors / exceptions were caught.
     **/
    int SDocModelItemICM::main(int argc, char *argv[]){

        // Defaults for connecting to the database.
        char * database = SConnectDisconnectICM_DEFAULT_DATABASE;
        char * userName = SConnectDisconnectICM_DEFAULT_USERNAME;
        char * password = SConnectDisconnectICM_DEFAULT_PASSWORD;

        //------------------------------------------------------------
        // Checking for input parameters
        //--------------------------------------------------------------
        if (argc < 4) { // if not all 3 arguments were specified, use defaults and report correct usage.
            cout << "Usage: "                                                       << endl;
            cout << "  SDocModelItemICM <database> <userName> <password>"           << endl;
            cout << "  *** Some parameters not specified, using defaults..."        << endl;
            cout << endl;
        } else {  // otherwise enough parameters were specified, use the first 3 for the 3 parameters.
            if (argc > 1) database = argv[1];
            if (argc > 2) userName = argv[2];
            if (argc > 3) password = argv[3];
        }//end else
        char * ver = SConnectDisconnectICM_VERSION;

        cout << "==========================================="     << endl;
        cout << "IBM DB2 Content Manager                v" << ver << endl;
        cout << "Sample Program:  SResourceItemICM"               << endl;
        cout << "-------------------------------------------"     << endl;
        cout << " Database: " << database                         << endl;
        cout << " UserName: " << userName                         << endl;
        cout << "===========================================\n"   << endl;

        try{
            //-------------------------------------------------------------
            // Connect to datastore
            //-------------------------------------------------------------
            // See Sample SConnectDisconnectICM for more information
            cout << "Connecting to datastore (Database '" << database << "', UserName '" << userName << "')..." << endl;

                DKDatastoreICM* dsICM = new DKDatastoreICM();  // Create new datastore object.
                dsICM->connect(database,userName,password,""); // Connect to the datastore.

            cout << "Connected to datastore (Database '" << dsICM->datastoreName() << "', UserName '" << dsICM->userName() << "')." << endl;

            //-------------------------------------------------------------
            // Create a Document
            //-------------------------------------------------------------
            // Please refer to SItemCreationICM for all non-resource related
            // information.
            //
            // Remember that "S_docModel" is an Item Type defined in
            // SItemTypeCreationICM.
            cout << "Creating a Document..." << endl;

                DKDDO* ddoDocument = dsICM->createDDO("S_docModel", DK_CM_DOCUMENT);

            cout << "Created a Document." << endl;

            //-------------------------------------------------------------
            // Create Parts
            //-------------------------------------------------------------
            // Please refer to SResourceItemCreationICM for all resource
            // related information.
            cout << "Creating Parts..." << endl;

                DKLobICM*   base      = (DKLobICM*)  dsICM->createDDO("ICMBASE",     DK_ICM_SEMANTIC_TYPE_BASE);
                DKTextICM*  baseText1 = (DKTextICM*) dsICM->createDDO("ICMBASETEXT", DK_ICM_SEMANTIC_TYPE_BASE);
                DKTextICM*  baseText2 = (DKTextICM*) dsICM->createDDO("ICMBASETEXT", DK_ICM_SEMANTIC_TYPE_BASE);

                cout << "Setting Parts' MIME Type / Type of Contents..." << endl;
                base->setMimeType("application/msword");
                baseText1->setMimeType("text/plain");
                baseText2->setMimeType("text/plain");

                cout << "Loading Content into Parts..." << endl;
                base->setContentFromClientFile("SResourceItemICM_Document1.doc"); // Load the file into memory.
                baseText1->setContentFromClientFile("SResourceItemICM_Text1.txt");
                baseText2->setContentFromClientFile("SResourceItemICM_Text2.txt");

            cout << "Created Parts." << endl;

            //-------------------------------------------------------------
            // Accessing the DKParts Attribute
            //-------------------------------------------------------------
            // The DKParts attribute is accessed the same way shown in 
            // SItemCreationICM & SItemRetrievalICM.  Note that this is
            // the same way a DKFolder collection is accessed.
            //
            // This attribute can be found programatically as described in
            // Sample SItemRetrievalICM.
            cout << "Accessing the DKParts Attribute..." << endl;
            
                DKParts* dkParts = (DKParts*)(dkCollection*) ddoDocument->getData(ddoDocument->dataId(DK_CM_NAMESPACE_ATTR,DK_CM_DKPARTS)); 
                
                // Important:  If the DDO is not retrieved, the above may
                //             fail due to a null pointer exception.  The
                //             following accounts for this.
                
                short dataid = ddoDocument->dataId(DK_CM_NAMESPACE_ATTR,DK_CM_DKPARTS); 
                
                if(dataid==0)
                    throw DKException("No DKParts Attribute Found!  DDO is either not an Item of a Document Model classified Item Type or Document has not been explicitly retrieved.");
                    
                dkParts = (DKParts*)(dkCollection*) ddoDocument->getData(dataid); 

            cout << "Accessed the DKParts Attribute." << endl;

            //-------------------------------------------------------------
            // Adding Parts to Document
            //-------------------------------------------------------------
            // Use the "Element" methods to group all updates into a single
            // call to the Library Server.  "Member" methods, if available,
            // are immediately persistent, but at a cost of a Library Server
            // call for every single call.
            cout << "Adding Parts to Document..." << endl;
            
                dkParts->addElement((dkDataObjectBase*)(DKDDO*)base);
                dkParts->addElement((dkDataObjectBase*)(DKDDO*)baseText1);
                dkParts->addElement((dkDataObjectBase*)(DKDDO*)baseText2);

            cout << "Added Parts to Document." << endl;

            //-------------------------------------------------------------
            // Adding New Document to Persistent Datastore
            //-------------------------------------------------------------
            cout << "Adding New Document to Persistent Datastore..." << endl;
        
                ddoDocument->add(); 

            cout << "Added New Document to Persistent Datastore." << endl;

            //-------------------------------------------------------------
            // Printing Document Part List
            //-------------------------------------------------------------
            cout << "Printing Document Part List..." << endl;

                printDocumentPartList(ddoDocument);

            cout << "Printed Document Part List." << endl;

            //-------------------------------------------------------------
            // Check out the Document for Modification
            //-------------------------------------------------------------
            cout << "Checking Out / Locking Document Before Update..." << endl;

                dsICM->checkOut(ddoDocument);  // Must check out / lock before updating.

            cout << "Checked Out / Locked Docuent Before Update." << endl;

            //-------------------------------------------------------------
            // Delete a Part / Remove a Part 
            //-------------------------------------------------------------
            cout << "Deleting a Part / Removing a Part..." << endl;

                // Look for the Part to Remove.  In this case, we will look for 
                // the baseText2.
                dkIterator* iter = dkParts->createIterator();
                while(iter->more()){                                        // while there are still items, continue searching
                    DKLobICM* part = (DKLobICM*) iter->next()->value();     // Move pointer to next element & return that object.
                    if(strcmp(((DKPidICM*)part->getPidObject())->pidString(),((DKPidICM*)baseText2->getPidObject())->pidString())==0){

                        dkParts->removeElementAt(*iter);  // Now that we found baseText2, remove it.

                    }
                }
                delete(iter);   // Free Memory
            
            cout << "Removed a Part from a Document." << endl;

            //-------------------------------------------------------------
            // Make Changes Persistent
            //-------------------------------------------------------------
            cout << "Making Changes Persistent..." << endl;
        
                ddoDocument->update(DK_CM_CHECKIN);   // Don't forget to explicitly check in when finished.

            cout << "Made Changes Persistent." << endl;

            //-------------------------------------------------------------
            // Recreate DDO to Demonstrate Retrieval
            //-------------------------------------------------------------
            // When recreating a DDO for an existing item in C++, always
            // use createDDOFromPID() that states "FromPID", not any of
            // the deprecated "createDDO" variations.  This ensures a clean,
            // blank DDO and avoids uncessary initialization of metadata
            // structures with defaults, intiial values, and empty collections.
            //
            // For existing items, always recreate as blank DDOs and use
            // retrieve to populate accurate data.  This way you can easily
            // distinguish between an accurate value that has been retrieved
            // from an initialial or default state.  See SItemRetrievalICM
            // sample for more information on this important topic.
            cout << "Recreating Document DDO..." << endl;

                DKDDO* original = ddoDocument;                                            // Save a reference to free the original after the DDO is recreated.  The PID object is needed to pass to createDDOFromPID and therefore the DDO that owns the PID cannot be deleted until after recreated.
                DKPidICM* pid   = ((DKPidICM*)ddoDocument->getPidObject());               // Get the PID object
                ddoDocument     = dsICM->createDDOFromPID(pid); // *ALWAYS* use "FromPID" // Recreate Blank DDO Using PID String.
                delete(original);                                                            // Free the original DDO that we have now replaced in pointer "ddoDocument".

                // OR suppose you want to have the PID string created and parsed:

                DKString pidString = ((DKPidICM*)ddoDocument->getPidObject())->pidString();  // Get the PID string
                delete(ddoDocument);                                                         // Free the original DDO since we are going to replace the pointer "ddoDocument".  We can free it before recreating because we already generated the PID string and do not need a reference to the PID objects anymore.
                ddoDocument = dsICM->createDDOFromPID(pidString); // *ALWAYS* use "FromPID"  // Recreate Blank DDO Using PID String.

            cout << "Recreated Document DDO." << endl;

            //-------------------------------------------------------------
            // Retrieving Document
            //-------------------------------------------------------------
            // This section covers retrieving a Document Item with the 
            // DKParts attribute / collection.
            //
            // Please review SItemRetrievalICM's retrieval option documentation.
            cout << "Retrieving Document..." << endl;

                // Suppose we want to retrieve the document attributes,
                // parts list, and parts attributes. (However consider your
                // scenario carefully for optimal retrieve option use).
                DKRetrieveOptionsICM* dkRetrieveOptions = DKRetrieveOptionsICM::createInstance(dsICM);
                dkRetrieveOptions->baseAttributes(TRUE);
                dkRetrieveOptions->partsList(TRUE);
                dkRetrieveOptions->partsAttributes(TRUE);
            
                ddoDocument->retrieve(dkRetrieveOptions->dkNVPair(),dkRetrieveOptions->dkNVPairLength());

            cout << "Retrieved Document." << endl;

            //-------------------------------------------------------------
            // Printing Document Part List
            //-------------------------------------------------------------
            cout << "Printing Document Part List..." << endl;

                printDocumentPartList(ddoDocument);

            cout << "Printed Document Part List." << endl;

            //-------------------------------------------------------------
            // Deleting Document
            //-------------------------------------------------------------
            cout << "Deleting Document..." << endl;

                ddoDocument->del();
                
                delete(ddoDocument);       // Free Memory
                delete(dkRetrieveOptions); // Free Memory

            cout << "Deleted Document." << endl;

            //-------------------------------------------------------------
            // Disconnect from datastore & Destroy Reference
            //-------------------------------------------------------------
            // See Sample SConnectDisconnectICM for more information
            cout << "Disconnecting from datastore & destroying reference..." << endl;            

                dsICM->disconnect();
                delete(dsICM);

            cout << "Disconnected from datastore & destroying reference." << endl;
    
            //-------------------------------------------------------------
            // Sample program completed without exception
            //-------------------------------------------------------------
            cout << "\n==========================================" << endl;
            cout << "Sample program completed."                    << endl;
            cout << "==========================================\n" << endl;
        }
        //------------------------------------------------------------
        // Catch & Print Exceptions        
        //------------------------------------------------------------
        catch (DKException &exc){
            SConnectDisconnectICM::printException(exc);    // Print the exception to the screen
            return(SConnectDisconnectICM_RC_SAMPLE_FAILED);  // Report using return code that it failed
        }
        return(SConnectDisconnectICM_RC_SAMPLE_PASSED);    // If no exceptions were thrown, return a code to indicate passed.
    }// end main

    //=================================================================
    // Wrapper Functions
    //=================================================================
    // The following are wrapper functions for functionality covered
    // in this sample.  These functions can be used by other samples.

   /**
    * Print Document Part List
    * @param document  Item of a Document Model classified Item Type.
    **/
    void SDocModelItemICM::printDocumentPartList(DKDDO* document){
        
        DKString documentItemId = ((DKPidICM*)document->getPidObject())->getItemId();

        // Get the DKParts object.        
        short dataid = document->dataId(DK_CM_NAMESPACE_ATTR,DK_CM_DKPARTS); 
        if(dataid==0)
            throw DKException("No DKParts Attribute Found!  DDO is either not an Item of a Document Model classified Item Type or Document has not been explicitly retrieved.");
        DKParts* dkParts = (DKParts*)(dkCollection*) document->getData(dataid); 
        
        // Print the list        
        cout << "Document (" << documentItemId << ") Parts:" << endl;
        dkIterator* iter = dkParts->createIterator();       // Create an Iterator go to through DDO Collection.
        while(iter->more()){                                // While there are items still to be printed, continue
            DKDDO* part = (DKDDO*) iter->next()->value();   // Move pointer to next element & return that object.
            cout << "     Item Id:  " << ((DKPidICM*)part->getPidObject())->getItemId() << " (" << ((DKPidICM*)part->getPidObject())->getObjectType() << ")" << endl;
        }
        delete(iter);      // Free Memory
    }