Multiple Item Clone
From C3 Wiki
[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;
}
