Multiple Item Clone

From C3 Wiki

Jump to: navigation, search

[edit] Summary

This method is identical to CustomUtils.clone() with the exception that it can be called multiple times and by different entities during a single transaction. In CustomUtils.clone(), the WOM variable entType+"_clone_shallowlist" is not replaceable. This prevents any change in its value for the duration of transaction. It's important during a large clone operation (for instance a study clone for amendment) for this variable's value's not to be lost or replaced. In other circumstances though, it's necessary to clone multiple entities during one transaction. Setting the variable as replaceable in this method allows multiple calls within a single transaction.

Warning: while this code works for the example at hand, it does not necessarily conform to best practices. Consult Click Professional Services if you have another scenario that requires cloning of multiple items, especially complex entities like projects. See Batch Entity Cloning for a supported method.

[edit] Code

The following is an example of calling CustomUtils.MultiItemClone() from inside of a loop:

	function copyDocument(set, setType) {
	   if(set==null) {
		  return(null);
	   }
	   var result = ApplicationEntity.getTypeNamed(setType).createEntitySet();
	   var col = set.elements();
	   
	   wom.log("Archive Study Data to CR: there are " +col.count()+ " items in this document set.");
	   for(var i=1;i<=col.count();i++){
		  wom.log("Archive Study Data to CR: Start clone for item " +i+ ".");
		  //result.addElement(CustomUtils.clone(col.item(i),null));
		  var newDoc = CustomUtils.MultipleItemClone(col.item(i), null);
		  result.addElement(newDoc);
		  wom.log("Archive Study Data to CR: item " +i+ " added to set");
	   }
	   return(result);
	}

The following is the code for this method on the CustomUtils entity type. Like CustomUtils.clone() it is a per type script.

function MultipleItemClone(ent, shallowCopyList)
{
	var e;
	try 
	{
		if (ent == null) throw(new Error(-1, "Cannot clone null reference"));
		
		// Start
		WOM.Log("CustomUtils.clone(): Start");
		WOM.Log("CustomUtils.clone(): Cloning "+ent);
		
		// Default shallow copy list		
		var shallowList = new Array("Company", "Person", "Country", "Container", "Region", 
						"State", "TimeZone", "UserRole", "Project");		
		var table = WOM.createPersistentObject("com.webridge.entity.util.Table");
		table.setElementType("com.webridge.entity.StringType");
		
		// Build shallow copy hashtable
		// Add defaults 
		var i = 0;
		if (shallowCopyList != null) {
			for (i = 0; i < shallowCopyList.length; i++) {
				if (!table.containsKey(shallowCopyList[i])) {		
					table( shallowCopyList[i] ) = "" ;
				}
			}
		}
		// Add user specified list
		for (i = 0; i < shallowList.length; i++) {
			if (!table.containsKey(shallowList[i])) {
					table(shallowList[i]) =  "" ;
			}
		}
		// Clone
		var entType = ent.getType();
		
		// Set shallow copy table in context
		//WOM.putContext(entType+"_clone_shallowlist", table, false);
		//SEM: made wom variable replacable to allow multiple calls
		WOM.putContext(entType+"_clone_shallowlist", table, true);

		var attributes = AttributeDescription.getAllForEType(entType);
		var newEntity = _clone(ent, entType, attributes, ent);

		// Done
		WOM.Log("CustomUtils.clone(): New entity "+newEntity);
		WOM.Log("CustomUtils.clone(): Done");

		return newEntity;
	} catch (e) {
		WOM.log("EXCEPTION CustomUtils.clone: " + e.description);
		throw(e);
	}
}

function _clone(ent, entType, attributes, cloneRoot)
{
	var e;
	try 
	{
		//WOM.Log("Cloning entity "+ent+" of type "+entType+", # of attributes "+attributes.count());
		
		// Setup lookup/tracking hashtables
		var shallow = WOM.getContext(cloneRoot.getType()+"_clone_shallowlist");
		var entityCloneMap = WOM.getContext(cloneRoot+"_entityclone_map");
		var esetCloneMap = WOM.getContext(cloneRoot+"_esetclone_map");
		
		if (entityCloneMap == null) {
			entityCloneMap = WOM.createPersistentObject("com.webridge.entity.util.Table");
			entityCloneMap.setElementType("com.webridge.entity.ObjectType");
			WOM.putContext(cloneRoot+"_entityclone_map", entityCloneMap, true);
		}
		if (esetCloneMap == null) {
			esetCloneMap = WOM.createPersistentObject("com.webridge.entity.util.Table");
			esetCloneMap.setElementType("com.webridge.entity.StringType");
			WOM.putContext(cloneRoot+"_esetclone_map", esetCloneMap, true);
		}

		// Create a transient entity of the specified entities type
		var newEntity = WOM.createTransientEntity(entType+"");
		
		// Save new entity.
		entityCloneMap(ent) = newEntity;
		
		// Copy each attribute from original to new entity
		var i = 0;
		for (i = 1; i <= attributes.count(); i++)
		{
			var attributeName = null;
			var value = null;
			var attrDataType = null;
			var isEntityRef = false;
			var isSetRef = false;
			var isDocContent = false;
			var attrType = null;
			var prefix = null;
			
			attributeName = attributes.item(i).qualifiedName;
			value = ent.getQualifiedAttribute(attributeName);
			
			if (value == null) continue;
			
			var adDataType = attributes.item(i).getDataType();	
			if (adDataType.indexOf("/") != -1) {	
				attrType = adDataType.split("/")[1];
				prefix = adDataType.split("/")[0]
			} else {
				attrType = adDataType;
				prefix = "Scalar";
			}
			
			isEntityRef = (prefix == "Entity");
			isSetRef = (prefix == "Set");
			isDocContent = (attrType == "DocumentContent");
			
			if ((!isEntityRef && !isSetRef) || isDocContent) {
				// WOM.Log("Scalar valued attribute: "+attributeName+" of type "+attrType);
				newEntity.setQualifiedAttribute(attributeName, value);	
	
				if (isDocContent) {
					var docProxy = newEntity.getQualifiedAttribute(attributeName);
					if (docProxy != null) {
						WOM.Log("Adding userCanRead() on "+newEntity.toDisplayString()+" to "+docProxy);
						docProxy.addAccessValidationMethod(newEntity, "userCanRead");					    	
					}
				}
				continue;
			}



			var refType = null;
			var refTypeAttrs = null;
			var newRefValue = null;

			if (isEntityRef) {
				refType = value.getType();
			} else if (isSetRef) {
				refType = value.getEntityType();
			}
			
			var makeShallowCopy = shallowCopy(refType, shallow);			

			if (!isSetRef && makeShallowCopy)
			{
				// WOM.Log("Shallow copy or DocumentContent attribute: "+attributeName+" of type "+refType);
				// Check if we have cloned it (typically root)
				var newValue = null;
				if (entityCloneMap.containsKey(value)) {
					newValue = entityCloneMap(value);
				}
				if (newValue != null) {
					newEntity.setQualifiedAttribute(attributeName, newValue);
				} else {
					newEntity.setQualifiedAttribute(attributeName, value);
				}
				continue;
			}

			refTypeAttrs = AttributeDescription.getAllForEType(refType);
			if (isEntityRef) {
				if (entityCloneMap.containsKey(value)) {
					newRefValue = entityCloneMap(value);
				}
				if (newRefValue == null) {
					newRefValue = _clone(value, refType, refTypeAttrs, cloneRoot);
				} 
				newEntity.setQualifiedAttribute(attributeName, newRefValue);
			} else if (isSetRef) {
				var newSet = null;
				var elems = null;
				var k = 1;
				if (esetCloneMap.containsKey(value)) {
					var newSetPoRefStr = esetCloneMap(value);
					newSet = EntityUtils.getObjectFromString(newSetPoRefStr);
				}

				if (newSet != null) {
					newEntity.setQualifiedAttribute(attributeName, newSet);
					continue;
				}

				newSet = refType.createEntitySet();
				esetCloneMap(value) = newSet+"";
				
				newEntity.setQualifiedAttribute(attributeName, newSet);
				if (makeShallowCopy) {
					newSet.addAll(value);
					continue;
				}   
				elems = value.elements();
				for (k = 1; k <= elems.count(); k++) {
					if (entityCloneMap.containsKey(elems.item(k))) {
						newRefValue = entityCloneMap(elems.item(k));
					}
					if (newRefValue == null && makeShallowCopy) {
						newRefValue = elems.item(k);
					} 
					if (newRefValue == null) {
						newRefValue = _clone(elems.item(k), refType, refTypeAttrs, cloneRoot);
					}
					newSet.addElement(newRefValue);
					newRefValue = null;
				}
			}
		}
		if (entType.inheritsFrom(Resource) || entType.inheritsFrom(CustomDataType)) {
			if (entType.hasAttributeNamed("ID")) 
			{
				var newID = ApplicationEntity.getID(entType+"");
				newEntity.Id = newID;
				//WOM.Log("Generated new id for "+entType+" old id "+ent.ID+" new id "+newID);
			}
		}
		if (entType.hasAttributeNamed("owningEntity")) {
			//WOM.Log("Setting owning entity on type "+entType+" old value "+ent.owningEntity+" new "+entityCloneMap(cloneRoot));
			newEntity.owningEntity = entityCloneMap(cloneRoot);
		}
		newEntity.registerEntity();
		return newEntity;
	} catch(e) {
		WOM.Log("EXCEPTION CustomUtils._clone: "+e.description);
		thow(e);
	}
}

function shallowCopy(etype, shallowCopyLookup)
{
	return (shallowCopyLookup.containsKey(etype) || 
				(!etype.inheritsFrom(CustomDataType) &&
				 etype+"" != "Document" &&
				 etype+"" != "ResourceHistory" 
				) ||
				isSelectionType(etype)
			);
}

function isSelectionType(etype) 
{
	var islookup = (etype.inheritsFrom(CustomDataType) && 
				 			etype.hasAttributeNamed("_usage") &&
				 			etype._usage == "selection"
				 			)
	return islookup;
}

Wake Forest