/*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 Information:
    Doc Model Items are an extension of non-resource items.  Doc Model Parts
    are resource items.  Therefore, both the standard item & resource item
    samples are pre-requisite 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.
               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.
        
        When adding and removing parts, there are multiple way to handle the 
        content.  The typical ways are:
          1) Loading the content via file name
          2) Loading the content via a stream
          3) Loading the content into memory in the DDO.
        Although loading the content into memory is the simplest to do, this
        approach is not recommended for any production application.  As loading
        content into memory takes up JVM heap memory for each user that is
        doing this, it is very easy to get out of memory errors when there are
        multiple users or if files are large.  For this reason, either option
        1 or 2, should be used for any production application.
        
        All three methods are demonstrated in this sample.
        
    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.        
        
        When retrieving the content contained in parts, there are multiple way to 
        handle the content.  The typical ways are:
          1) Storing the content in a file
          2) Getting a stream that will return the content
          3) Loading the content into memory in the DDO, and accessing the memory buffer.
        Although loading the content into memory is the simplest to do, this
        approach is not recommended for any production application.  As loading
        content into memory takes up JVM heap memory for each user that is
        doing this, it is very easy to get out of memory errors when there are
        multiple users or if files are large.  For this reason, either option
        1 or 2, should be used for any production application.
        
        All three methods are demonstrated in this sample.
    
        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.
        
*******************************************************************************/

// Imports
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import com.ibm.mm.sdk.common.*;
import com.ibm.mm.sdk.server.*;

/************************************************************************************************
 *          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.
 *                    Please run the following samples.
 *                        - SResourceMgrDefSetDefaultICM
 *                        - SSMSCollectionDefSetDefaultICM
 *                    NOTE:  Sample SSampleModelBuildICM pulls together all of the above.  It 
 *                           must be edited first for the RM Configuration information.
 *                    ---------------------------------------------------------------------------
 *    FILES REQUIRED: SConnectDisconnectICM.java
 *                    SResourceItemICM_Document1.doc
 *                    SResourceItemICM_Text1.txt
 *                    SResourceItemICM_Text2.txt
 ************************************************************************************************/
public class SDocModelItemICM{

    // Using constants for File Names
    public static final String LOB_FILE1_FILENAME         = "SResourceItemICM_Document1.txt";
    public static final String TEXT_FILE1_FILENAME        = "SResourceItemICM_Text1.txt";
    public static final String TEXT_FILE2_FILENAME        = "SResourceItemICM_Text2.txt";
    
    //-------------------------------------------------------------
    // Main
    //-------------------------------------------------------------
    /**
     * Run the Sample.
     * @param argv[] String Array containing arguments.  Optional arguments are <databse> <userName> <password>.
     */
    public static void main(String argv[]) throws DKException, Exception{
    
        // Defaults for connecting to the database.
        String database = SConnectDisconnectICM.DEFAULT_DATABASE;
        String userName = SConnectDisconnectICM.DEFAULT_USERNAME;
        String password = SConnectDisconnectICM.DEFAULT_PASSWORD;
        
        //------------------------------------------------------------
        // Checking for input parameters
        //--------------------------------------------------------------
        if (argv.length < 3) { // if not all 3 arguments were specified, use defaults and report correct usage.
            System.out.println("Usage: " );
            System.out.println("  java SDocModelItemICM <database> <userName> <password>" );
            System.out.println("  *** Some parameters not specified, using defaults..." );
            System.out.println("");
        } else {  // otherwise enough parameters were specified, use the first 3 for the 3 parameters.
            if (argv.length > 0) database = argv[0];
            if (argv.length > 1) userName = argv[1];
            if (argv.length > 2) password = argv[2];
        }//end else
        String ver = SConnectDisconnectICM.VERSION;
        
        System.out.println("===========================================");
        System.out.println("IBM DB2 Content Manager                v"+ver);
        System.out.println("Sample Program:  SDocModelItemICM");
        System.out.println("-------------------------------------------");
        System.out.println(" Database: "+database);
        System.out.println(" UserName: "+userName);
        System.out.println("===========================================");
        
        try{
            //-------------------------------------------------------------
            // Connect to datastore
            //-------------------------------------------------------------
            // See Sample SConnectDisconnectICM for more information
            System.out.println("Connecting to datastore (Database '"+database+"', UserName '"+userName+"')...");

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

            System.out.println("Connected to datastore (Database '"+dsICM.datastoreName()+"', UserName '"+dsICM.userName()+"').");

            //-------------------------------------------------------------
            // Create a Document
            //-------------------------------------------------------------
            // Please refer to SItemCreationICM for all non-resource related
            // information.
            //
            // Remember that "S_docModel" is an Item Type defined in
            // SItemTypeCreationICM.
            System.out.println("Creating a Document...");

                DKDDO ddoDocument = dsICM.createDDO("S_docModel", DKConstant.DK_CM_DOCUMENT);

            System.out.println("Created a Document.");

            //-------------------------------------------------------------
            // Create Parts
            //-------------------------------------------------------------
            // Please refer to SResourceItemCreationICM for all resource
            // related information.
            System.out.println("Creating Parts...");

                DKLobICM   base      = (DKLobICM)  dsICM.createDDO("ICMBASE",     DKConstantICM.DK_ICM_SEMANTIC_TYPE_BASE);
                DKTextICM  baseText1 = (DKTextICM) dsICM.createDDO("ICMBASETEXT", DKConstantICM.DK_ICM_SEMANTIC_TYPE_BASE);
                DKTextICM  baseText2 = (DKTextICM) dsICM.createDDO("ICMBASETEXT", DKConstantICM.DK_ICM_SEMANTIC_TYPE_BASE);

                System.out.println("Setting Parts' MIME Type / Type of Contents...");
                base.setMimeType("application/msword");
                baseText1.setMimeType("text/plain");
                baseText2.setMimeType("text/plain");

                // Important:  The three methods of loading content are shown below.
                //             Content should never be loaded into memory for any 
                //             production application.
                
                // Method 1 - Add content from file
                String fileName = "SResourceItemICM_Document1.doc";                // Load content from file
                DKNVPair[] location = new DKNVPair[1];
                location[0] = new DKNVPair(DKConstantICM.FILE, fileName);
                
                base.setAddLocation(location);
                base.setOrgFileName(fileName);
                
                // Method 2 - Add content from stream
                fileName = "SResourceItemICM_Text1.txt";                           // Load content from stream
                InputStream iStream = new FileInputStream(fileName);
                File file = new File(fileName);
                //..Set the stream and the length that will be used to add the part
                location = new DKNVPair[2];
                location[0] = new DKNVPair(DKConstantICM.STREAM, iStream);
                location[1] = new DKNVPair(DKConstantICM.LENGTH, file.length());

                baseText1.setAddLocation(location);
                baseText1.setOrgFileName(fileName);
                
                // Method 3 - Add content from in memory buffer.  Not recommended.
                baseText2.setContentFromClientFile("SResourceItemICM_Text2.txt");  // Load content into memory

            System.out.println("Created Parts.");

            //-------------------------------------------------------------
            // 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.
            System.out.println("Accessing the DKParts Attribute...");
            
                DKParts dkParts = (DKParts) ddoDocument.getData(ddoDocument.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,DKConstant.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(DKConstant.DK_CM_NAMESPACE_ATTR,DKConstant.DK_CM_DKPARTS); 
                
                if(dataid==0)
                    throw new Exception("No DKParts Attribute Found!  DDO is either not a Document Model classified Item or Document has not been explicitly retrieved.");
                    
                dkParts = (DKParts) ddoDocument.getData(dataid); 

            System.out.println("Accessed the DKParts Attribute.");

            //-------------------------------------------------------------
            // 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.
            System.out.println("Adding Parts to Document...");
            
                dkParts.addElement(base);
                dkParts.addElement(baseText1);
                dkParts.addElement(baseText2);

            System.out.println("Added Parts to Document.");

            //-------------------------------------------------------------
            // Adding New Document to Persistent Datastore
            //-------------------------------------------------------------
            System.out.println("Adding New Document to Persistent Datastore...");
        
                ddoDocument.add(); 

            System.out.println("Added New Document to Persistent Datastore.");

            //-------------------------------------------------------------
            // Printing Document Part List
            //-------------------------------------------------------------
            System.out.println("Printing Document Part List...");

                printDocumentPartList(ddoDocument);

            System.out.println("Printed Document Part List.");

            //-------------------------------------------------------------
            // Check out the Document for Modification
            //-------------------------------------------------------------
            System.out.println("Checking Out / Locking Document Before Update...");

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

            System.out.println("Checked Out / Locked Docuent Before Update.");

            //-------------------------------------------------------------
            // Delete a Part / Remove a Part 
            //-------------------------------------------------------------
            System.out.println("Deleting a Part / Removing a Part...");

                // 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();     // Move pointer to next element & return that object.
                    if(part.getPidObject().pidString().compareTo(baseText2.getPidObject().pidString())==0){

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

                    }
                }
            
            System.out.println("Removed a Part from a Document.");

            //-------------------------------------------------------------
            // Make Changes Persistent
            //-------------------------------------------------------------
            System.out.println("Making Changes Persistent...");
        
                ddoDocument.update(DKConstant.DK_CM_CHECKIN);   // Don't forget to explicitly check in when finished.

            System.out.println("Made Changes Persistent.");

            //-------------------------------------------------------------
            // Recreate DDO to Demonstrate Retrieval
            //-------------------------------------------------------------
            // 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 initial or default state.  See SItemRetrievalICM
            // sample for more information on this important topic.
            System.out.println("Recreating Document DDO...");
            
                DKPidICM pid = (DKPidICM) ddoDocument.getPidObject();                                 // Get the PID object
                ddoDocument = dsICM.createDDOFromPID(pid);  // *ALWAYS* use "FromPID" // Recreate Blank DDO Using PID String.

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

                String pidString = ddoDocument.getPidObject().pidString();                 // Get the PID string
                ddoDocument = dsICM.createDDOFromPID(pidString); // *ALWAYS* use "FromPID" // Recreate Blank DDO Using PID String.

            System.out.println("Recreated Document DDO.");

            //-------------------------------------------------------------
            // Retrieving Document
            //-------------------------------------------------------------
            // This section covers retrieving a Document Item with the 
            // DKParts attribute / collection.
            //
            // Please review SItemRetrievalICM's retrieval option documentation.
            System.out.println("Retrieving Document...");

                // 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);
                
                // Parts attributes are normally only needed when content will be retrieved, so this
                // option should not be used for a search result, as it will return information that
                // likely will not be used for all items in the result.  If just basic part 
                // information (Semantic Type, Mime Type, Original File Name, and file size) is needed, 
                // then add this line to optimize retrieval:
                // dkRetrieveOptions.behaviorPartsAttributesCoreOnly();
                dkRetrieveOptions.partsAttributes(true);                
                                            
                ddoDocument.retrieve(dkRetrieveOptions.dkNVPair());

            System.out.println("Retrieved Document.");

            //-------------------------------------------------------------
            // Printing Document Part List
            //-------------------------------------------------------------
            System.out.println("Printing Document Part List...");
            
                printDocumentPartList(ddoDocument);

            System.out.println("Printed Document Part List.");

            //-------------------------------------------------------------
            // Retrieving content from a document
            //-------------------------------------------------------------

            // Important:  The three methods of retrieving content are shown below.
            //             Content should never be loaded into memory for any 
            //             production application.
           
            //..Retrieve options to get content only
            dkRetrieveOptions = DKRetrieveOptionsICM.createInstance(dsICM);

            dkRetrieveOptions.resourceContent(true);
            dkRetrieveOptions.behaviorSkipResourceAttrRefresh(true);
            dkRetrieveOptions.behaviorSkipExistenceCheck(true);
            
            //..Get the parts that we will retrieve content for
            short   id = ddoDocument.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,DKConstant.DK_CM_DKPARTS);
            dkParts    = (DKParts)ddoDocument.getData(id);

            dkIterator it = dkParts.createIterator();

            //..Get the two parts from the collection
            DKLobICM retrPart  = (DKLobICM) it.next();
            DKLobICM retrPart2 = (DKLobICM) it.next();
            
            //..Method 1, Save content to a file
            retrPart.retrieve("fileRetr.out",dkRetrieveOptions.dkNVPair());
            
            //..Method 2, Get content as a stream
            InputStream is = retrPart.getInputStream(dkRetrieveOptions.dkNVPair(), -1, -1);
            
            writeStreamContentToFile(is, "streamRetr.out");

            //..Method 3, Load content into memory and access the in memory buffer - Not recommended
            retrPart2.retrieve(dkRetrieveOptions.dkNVPair());
            byte[] buffer = retrPart2.getContent();
            System.out.println("  Memory buffer size: " + buffer.length);
            
            //-------------------------------------------------------------
            // Deleting Document
            //-------------------------------------------------------------
            System.out.println("Deleting Document...");

                ddoDocument.del();

            System.out.println("Deleted Document.");

            //-------------------------------------------------------------
            // Disconnect from datastore & Destroy Reference
            //-------------------------------------------------------------
            // See Sample SConnectDisconnectICM for more information
            System.out.println("Disconnecting from datastore & destroying reference...");            

                dsICM.disconnect();
                dsICM.destroy();

            System.out.println("Disconnected from datastore & destroying reference.");

            //-------------------------------------------------------------
            // Sample program completed without exception
            //-------------------------------------------------------------
            System.out.println("\n==========================================");
            System.out.println("Sample program completed.");
            System.out.println("==========================================\n");
        }
        //------------------------------------------------------------
        // Catch & Print Exceptions        
        //------------------------------------------------------------
        catch (DKException exc){
            SConnectDisconnectICM.printException(exc);  // Print the exception using the function listed below.
            throw(exc);
        } catch (Exception exc) {
            SConnectDisconnectICM.printException(exc);  // Print the exception using the function listed below.
            throw(exc);
        }
    }// end main

    /**
     * Take the content from a stream and write it to a file.
     * 
     * @param is Stream with content that is returned from CM
     * @param fileName the name of the file to write the content to
     * @throws IOException 
     */
    private static void writeStreamContentToFile(InputStream is, String fileName) throws IOException {
      
      //..Write the content to a file
      System.out.println("  Write stream content to file: " + fileName);
      FileOutputStream fos = new FileOutputStream(fileName);

      long total = 0;
      byte[] buffer = new byte[1024*32];
      int count;
      while ((count = is.read(buffer)) != -1){
        fos.write(buffer, 0, count);
        total = total + count;
      }
      System.out.println("  Total bytes Read: " + total);
      System.out.println("  Closing streams...");
      is.close();
      fos.close();
    }

    //=================================================================
    // 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 based on Document Model classified Item Type.
    **/
    public static void printDocumentPartList(DKDDO document) throws Exception{
        
        String documentItemId = ((DKPidICM)document.getPidObject()).getItemId();

        // Get the DKParts object.        
        short dataid = document.dataId(DKConstant.DK_CM_NAMESPACE_ATTR,DKConstant.DK_CM_DKPARTS); 
        if(dataid==0)
            throw new Exception("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) document.getData(dataid); 
        
        // Print the list        
        System.out.println("Document ("+documentItemId+") Parts:");
        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();        // Move pointer to next element & return that object.
            System.out.println("     Item Id:  "+((DKPidICM)part.getPidObject()).getItemId() +" ("+part.getPidObject().getObjectType()+")");
        }
    }

}//end class SDocModelItemICM