/*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                                                              *
 *****************************************************************************/

// Imports

import com.ibm.mm.sdk.common.*;
import com.ibm.mm.sdk.server.*;
import java.io.*;
import java.util.ArrayList;

/************************************************************************************************
 *          FILENAME: SHoldItemICM.java
 *                    --------------------------------------------------------------------------------
 *       DESCRIPTION: An item type which is enabled the hold container option is the hold container
 *       			  item type. Items created under hold container item type are hold containers.
 *
 *       			  An item type which is enabled the on-hold option is the on-hold item type. Items
 *       			  created under on-hold item type can be put into hold containers. An item is
 *                    called on-hold item when it is put into hold container.
 *
 *                    Both hold container option and on-hold option can not be enabled on document
 *                    part item type classification.
 *
 *                    User should have the ItemRetentionAdmin, ItemSetHoldAdmin or SetItemHoldDelete
 *                    privilege on both hold container and on-hold item to perform addHold and removeHold
 *					  operations.
 *
 *					  On-hold items are not able to be deleted and reindexed(moved). But on-hold items can be
 *					  updated if the multiple version is enabled on the item type in which the on-hold
 *					  items are created. Update may fail if it causes the deletion on old on-hold version.
 *
 *					  User can query all on-hold items in a hold container and query all hold containers
 *					  one on-hold item may exist in.
 *
 *					  User can use on-hold indicator method to get the on-hold status for a given item.
 *                    ---------------------------------------------------------------------------------
 *     DEMONSTRATION: Enable hold container option on item type.
 *       			  Enable on-hold option on item type.
 *       			  Creating hold containers.
 *       			  Creating on-hold items.
 *       			  Adding items to hold containers.
 *       			  Removing items from hold containers.
 *       			  Using on-hold indicator method to show on-hold status for items.
 *       			  Querying on-hold items by specifying a hold container.
 *       			  Querying hold containers by specifying a on-hold item.
 *                    ---------------------------------------------------------------------------
 * COMMANDLINE USAGE: java SHoldItemICM <database> <userName> <password>
 *                    ---------------------------------------------------------------------------
 *     PREREQUISITES: The Data Model must be defined.  If needed please run the following Samples
 *                        - SItemTypeCreationICM
 *                    ---------------------------------------------------------------------------
 *    FILES REQUIRED: SConnectDisconnectICM.java
 *    				  SItemTypeCreationICM.java
 *    				  SLinksICM.java
 *    				  SItemRetrievalICM.java
 ************************************************************************************************/

public class SHoldItemsICM{

    //-------------------------------------------------------------
    // 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 SHoldItemsICM <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:  SHoldItemsICM");
        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 Hold Container Items Type
            //-------------------------------------------------------------
            System.out.println("Defining Hold Container Item Type 'S_HoldContainer'...");

            DKItemTypeDefICM holdItemType = new DKItemTypeDefICM(dsICM); // create a new Item Type Definition for hold container.
            holdItemType.setName("S_HoldContainer");
            holdItemType.setDescription("My Simple Hold Container Item Type");

            System.out.println("Retrieving attribute and adding it to Hold Container Item Type...");

            DKDatastoreDefICM dsDefICM = (DKDatastoreDefICM) dsICM.datastoreDef();
            DKAttrDefICM attr = (DKAttrDefICM) dsDefICM.retrieveAttr("S_varchar");
            holdItemType.addAttr(attr);

            System.out.println("Set item type hold container option...");

            holdItemType.setHoldContainer(true); // set hold container option for this item type before item type definition is persisted to datastore.
            holdItemType.add();                 //Save the Item Type Definition.


            //---------------------------------------------------------------------------
            //Enable on-hold options for item types
            //
            //Before putting items into hold container, enable on-hold option on item type
            //Except document part item type classification, we can enable on-hold option
            //on other item type classifications, including item, resource and document.
            //
            //---------------------------------------------------------------------------

            System.out.println("Retrieve item types and set their on-hold options...");
            DKItemTypeDefICM onHoldItemItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_simple"); //item
            onHoldItemItemType.setItemsCanBeOnHold(true);
            onHoldItemItemType.update();

            DKItemTypeDefICM onHoldResItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_text"); //resource
            onHoldResItemType.setItemsCanBeOnHold(true);
            onHoldResItemType.update();

            DKItemTypeDefICM onHoldDocItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_docModel"); //document
            onHoldDocItemType.setItemsCanBeOnHold(true);
            onHoldDocItemType.update();

            System.out.println("Creating Items");

	            DKDDO ddoItem   = dsICM.createDDO("S_simple", DKConstant.DK_CM_ITEM);
	            DKDDO ddoResource = dsICM.createDDO("S_text", DKConstant.DK_CM_RESOURCE);
	            DKDDO ddoDoc    = dsICM.createDDO("S_withChild", DKConstant.DK_CM_DOCUMENT);
	            DKDDO ddoFolder = dsICM.createDDO("S_simple", DKConstant.DK_CM_ITEM);

	            ddoItem.add();
	            ddoResource.add();
	            ddoDoc.add();
	            ddoFolder.add();

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

            //------------------------------------------------------------
            // Put items into hold container
            //------------------------------------------------------------

            DKDDO holdContainer1 = dsICM.createDDO("S_HoldContainer", DKConstant.DK_CM_ITEM);
            DKDDO holdContainer2 = dsICM.createDDO("S_HoldContainer", DKConstant.DK_CM_RESOURCE);

            holdContainer1.add();
            holdContainer2.add();

            ArrayList<DKPidICM> itemsToBeOnHold = new ArrayList<DKPidICM>();
            itemsToBeOnHold.add((DKPidICM)ddoItem.getPidObject());
            itemsToBeOnHold.add((DKPidICM)ddoResource.getPidObject());
            itemsToBeOnHold.add((DKPidICM)ddoDoc.getPidObject());
            itemsToBeOnHold.add((DKPidICM)ddoFolder.getPidObject());

            DKDatastoreExtICM dsExtICM = (DKDatastoreExtICM)dsICM.getExtension(DKConstant.DK_CM_DATASTORE_EXT);

            DKSequentialCollection errors =  dsExtICM.addHolds((DKPidICM)holdContainer1.getPidObject(), itemsToBeOnHold);
            SHoldItemsICM.printHoldErrors(errors);

            ArrayList<DKPidICM> itemsToBeOnHold2 = new ArrayList<DKPidICM>();
            itemsToBeOnHold2.add((DKPidICM)ddoItem.getPidObject());
            itemsToBeOnHold2.add((DKPidICM)ddoResource.getPidObject());

            errors = dsExtICM.addHolds((DKPidICM)holdContainer2.getPidObject(), itemsToBeOnHold2);
            SHoldItemsICM.printHoldErrors(errors);

            //------------------------------------------------------------------
            //Check item on-hold status
            //
            //Before we use on-hold indicator method to check the on-hold status for an object, which
            //has already existed before we invoke addHolds or removeHolds method,
            //we should make sure to invoke DKDDO.retrieve() method to refresh the attributes.
            //
            //------------------------------------------------------------------

            ddoItem.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoResource.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoDoc.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoFolder.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);

            System.out.println("On-hold flag in item ddoItem is: " + dsExtICM.isItemOnHold(ddoItem));
            System.out.println("On-hold flag in item ddoResource is: " + dsExtICM.isItemOnHold(ddoResource));
            System.out.println("On-hold flag in item ddoDoc is: " + dsExtICM.isItemOnHold(ddoDoc));
            System.out.println("On-hold flag in item ddoResource is: " + dsExtICM.isItemOnHold(ddoFolder));

            //------------------------------------------------------------------
            //Get on-hold items in a hold container
            //------------------------------------------------------------------

            DKRetrieveOptionsICM dkRetrieveOptions = DKRetrieveOptionsICM.createInstance(dsICM);
            dkRetrieveOptions.baseAttributes(true);
            dkRetrieveOptions.linksInbound(true);
            dkRetrieveOptions.linksOutbound(true);

            dsICM.retrieveObject(holdContainer1,dkRetrieveOptions.dkNVPair());

            ArrayList<DKLink> outboundLinks1 = SLinksICM.getOutboundLinks(holdContainer1);
            System.out.println("Get the DDO(s) in the hold container1:");
            for(DKLink link : outboundLinks1)
            {
            	if(!link.getTypeName().equalsIgnoreCase(DKConstantICM.DK_ICM_LINKTYPENAME_DKHOLD))
            		continue;
            	DKDDO ddo = (DKDDO)link.getTarget();
            	SItemRetrievalICM.printDDO(ddo);

            }

            dsICM.retrieveObject(holdContainer2,dkRetrieveOptions.dkNVPair());

            ArrayList<DKLink> outboundLinks2 = SLinksICM.getOutboundLinks(holdContainer2);
            System.out.println("Get the DDO(s) in the hold container2:");
            for(DKLink link : outboundLinks2)
            {
            	if(!link.getTypeName().equalsIgnoreCase(DKConstantICM.DK_ICM_LINKTYPENAME_DKHOLD))
            		continue;
            	DKDDO ddo = (DKDDO)link.getTarget();
            	SItemRetrievalICM.printDDO(ddo);
            }

            //------------------------------------------------------------------
            //Get hold containers in which the given item exists
            //------------------------------------------------------------------

            dsICM.retrieveObject(ddoItem,dkRetrieveOptions.dkNVPair());

            ArrayList<DKLink> inboundLinks1 = SLinksICM.getInboundLinks(ddoItem);
            System.out.println("The ddoItem exists in the following hold containers:");
            for(DKLink link : inboundLinks1)
            {
            	if(!link.getTypeName().equalsIgnoreCase(DKConstantICM.DK_ICM_LINKTYPENAME_DKHOLD))
            		continue;
            	DKDDO ddo = (DKDDO)link.getSource();
            	SItemRetrievalICM.printDDO(ddo);
            }

            dsICM.retrieveObject(ddoResource,dkRetrieveOptions.dkNVPair());

            ArrayList<DKLink> inboundLinks2 = SLinksICM.getInboundLinks(ddoResource);
            System.out.println("The ddoResource exists in the following hold containers:");
            for(DKLink link : inboundLinks2)
            {
            	if(!link.getTypeName().equalsIgnoreCase(DKConstantICM.DK_ICM_LINKTYPENAME_DKHOLD))
            		continue;
            	DKDDO ddo = (DKDDO)link.getSource();
            	SItemRetrievalICM.printDDO(ddo);
            }

			/*************************************************************************************************************
			 * How to construct hold related queries in Content Manager version 85:
			 * ---------------------------------------------------------------------
			 * Basically, hold related query runs the same way as LINK query, the way to construct a query is also similar.
			 * 1, The only big difference is : user MUST specify LINKTYPE=DKHold in query string when dealing with hold.
			 *    For example:
			 * 		Query on-hold items in a hold container
			 *          /hold/OUTBOUNDLINK[@LINKTYPE ="DKHold"]/@TARGETITEMREF => * [@Title LIKE \"Java%\"]
			 * 	    Query to Get all Holds for a held item:
			 *          /onhold/INBOUNDLINK[@LINKTYPE= "DKHold"]/@SOURCEITEMREF => *
			 *
 			 * 2, When no LINKTYPE is specified in a query string, the query will behavior the same as before,
 			 *    which means it will not involve table which has hold. This will benefit for existing queries.
 			 *    For example:
 			 *    	/MyItemType/OUTBOUNDLINK/@TARGETITEMREF => * [@VERSIONID = 1]
 			 *
 			 *    If user for any reason wants to query both Link and Hold related tables/items without specify LINKTYPE,
 			 *    a new query option was added for this. User can set query option DK_CM_PARM_USE_LINK_HOLD_UNION to true
 			 *    to force query to access Union table which contains both hold and normal LINKTYPEs.
 			 *
 			 * 3, If a query string specify more than one LINKTYPEs and one of them is DKHold, such as
 			 *    @LINKTYPE IN ("DKHold", "DkFolder"), an union view table will be used. It may have performance impact.
             ******************************************************************************************************************/

            //-------------------------------------------------------------------
            // Attempt to delete the on-hold item
            //-------------------------------------------------------------------
            try
            {
            	ddoItem.del();
            }
            catch(DKException e)
            {
            	if(e.errorCode() == DKConstantICM.DK_ICM_RC_DEL_ITEM_IN_HOLD_CONTAINER)
            		System.out.println("Fail to delete the on-hold item .");
            	else
            		System.out.println("Fail to delete item, exception:" + e.getMessage());
            }

            //-------------------------------------------------------------------
            //Remove item out from hold container
            //-------------------------------------------------------------------
            ArrayList<DKPidICM> itemsMovedHold1 = new ArrayList<DKPidICM>();
            itemsMovedHold1.add((DKPidICM)ddoItem.getPidObject());
            itemsMovedHold1.add((DKPidICM)ddoResource.getPidObject());
            itemsMovedHold1.add((DKPidICM)ddoDoc.getPidObject());
            itemsMovedHold1.add((DKPidICM)ddoFolder.getPidObject());

            System.out.println("Start to move items out from hold container1...");

            errors = dsExtICM.removeHolds((DKPidICM)holdContainer1.getPidObject(), itemsMovedHold1);
            SHoldItemsICM.printHoldErrors(errors);

            ddoItem.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoResource.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoDoc.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoFolder.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);

            System.out.println("Items moved from hold container1 is done...");

            System.out.println("After items moved out from hold container1:");
            System.out.println("On-hold flag in item ddoItem is: " + dsExtICM.isItemOnHold(ddoItem));
            System.out.println("On-hold flag in item ddoResource is: " + dsExtICM.isItemOnHold(ddoResource));
            System.out.println("On-hold flag in item ddoDoc is: " + dsExtICM.isItemOnHold(ddoDoc));
            System.out.println("On-hold flag in item ddoResource is: " + dsExtICM.isItemOnHold(ddoFolder));

            holdContainer1.del();

            //------------------------------------------------------------------
            // Delete hold container
            //
            // Hold container deletion will result in deletion on hold links
            // associated with this hold container.
            //------------------------------------------------------------------
            holdContainer2.del();

            ddoItem.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoResource.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoDoc.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);
            ddoFolder.retrieve(DKConstant.DK_CM_CONTENT_ATTRONLY);

            System.out.println("container2 deletion is done...");
            System.out.println("After container2 is deleted:");
            System.out.println("On-hold flag in item ddoItem is: " + dsExtICM.isItemOnHold(ddoItem));
            System.out.println("On-hold flag in item ddoResource is: " + dsExtICM.isItemOnHold(ddoResource));
            System.out.println("On-hold flag in item ddoDoc is: " + dsExtICM.isItemOnHold(ddoDoc));
            System.out.println("On-hold flag in item ddoResource is: " + dsExtICM.isItemOnHold(ddoFolder));

            System.out.println("Deleting Items.");
            ddoItem.del();
            ddoResource.del();
            ddoDoc.del();
            ddoFolder.del();

            System.out.println("Retrieve item types and set their on-hold options to false...");
            onHoldItemItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_simple"); //item
            onHoldItemItemType.setItemsCanBeOnHold(false);
            onHoldItemItemType.update();

            onHoldResItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_text"); //resource
            onHoldResItemType.setItemsCanBeOnHold(false);
            onHoldResItemType.update();

            onHoldDocItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_docModel"); //document
            onHoldDocItemType.setItemsCanBeOnHold(false);
            onHoldDocItemType.update();

            System.out.println("Delete Hold Container Item Type 'S_HoldContainer'...");

            holdItemType = (DKItemTypeDefICM) dsDefICM.retrieveEntity("S_HoldContainer"); // hold item type
            if (holdItemType != null){
             dsDefICM.del(holdItemType);
	    }

            //-------------------------------------------------------------
            // 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

    public static void printHoldErrors(DKSequentialCollection errors)
    {
     if(errors != null)
     {
      System.out.println(" Number of Hold errors found: " + errors.cardinality());
      dkIterator it = errors.createIterator();
      int i = 0;
      DKErrorInfoItemICM errorInfo = null;
      while(it.more())
      {
       try
       {
        i++;
        errorInfo = (DKErrorInfoItemICM)it.next();
        System.out.println(" Hold error["+i+"] " + errorInfo.toString());
       } catch (DKException e) {
        SConnectDisconnectICM.printException(e);
       }
      }
     }
    }

}//end class SHoldItemsICM.java