package org.archstudio.xadl.bna.utils; import java.io.Serializable; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import org.archstudio.sysutils.UIDGenerator; import org.archstudio.xarchadt.IXArchADT; import org.archstudio.xarchadt.IXArchADTFeature; import org.archstudio.xarchadt.IXArchADTTypeMetadata; import org.archstudio.xarchadt.ObjRef; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; public class XArchADTCopyPaste { /** * Performs a deep clone of an ObjRef, keeping references to other ObjRefs, but not cloning the referenced ObjRefs. * For a link this would mean that the cloned Link still points to the same interfaces, but does not clone the * interfaces themselves. */ protected ObjRef clone(IXArchADT xarch, ObjRef objRef, Map<ObjRef, ObjRef> oldNewRefs) { IXArchADTTypeMetadata typeMetadata = xarch.getTypeMetadata(objRef); ObjRef cloneRef = xarch.create(typeMetadata.getNsURI(), typeMetadata.getTypeName()); oldNewRefs.put(objRef, cloneRef); for (IXArchADTFeature feature : typeMetadata.getFeatures().values()) { switch (feature.getType()) { case ATTRIBUTE: xarch.set(cloneRef, feature.getName(), xarch.get(objRef, feature.getName())); break; case ELEMENT_SINGLE: Serializable s = xarch.get(objRef, feature.getName()); if (s instanceof ObjRef && !feature.isReference()) { s = clone(xarch, (ObjRef) s, oldNewRefs); } xarch.set(cloneRef, feature.getName(), s); break; case ELEMENT_MULTIPLE: for (Serializable m : xarch.getAll(objRef, feature.getName())) { if (m instanceof ObjRef && !feature.isReference()) { m = clone(xarch, (ObjRef) m, oldNewRefs); } xarch.add(cloneRef, feature.getName(), m); } break; } } return cloneRef; } /** * Goes through the ObjRef and replaces all IDs with new, unique identifiers. The goal is for the result to be * inserted into a document without any ID conflicts. */ protected void randomizeIDs(IXArchADT xarch, ObjRef objRef) { IXArchADTTypeMetadata typeMetadata = xarch.getTypeMetadata(objRef); for (IXArchADTFeature feature : typeMetadata.getFeatures().values()) { switch (feature.getType()) { case ATTRIBUTE: if ("id".equals(feature.getName())) { xarch.set(objRef, feature.getName(), UIDGenerator.generateUID()); } break; case ELEMENT_SINGLE: Serializable s = xarch.get(objRef, feature.getName()); if (s instanceof ObjRef && !feature.isReference()) { randomizeIDs(xarch, (ObjRef) s); } break; case ELEMENT_MULTIPLE: for (Serializable m : xarch.getAll(objRef, feature.getName())) { if (m instanceof ObjRef && !feature.isReference()) { randomizeIDs(xarch, (ObjRef) m); } } break; } } } /** * Goes through the Objref and replaces all references to old objRefs with references to the new objRefs. */ protected void updateReferences(IXArchADT xarch, ObjRef objRef, Map<ObjRef, ObjRef> oldNewRefs) { IXArchADTTypeMetadata typeMetadata = xarch.getTypeMetadata(objRef); for (IXArchADTFeature feature : typeMetadata.getFeatures().values()) { switch (feature.getType()) { case ATTRIBUTE: if ("id".equals(feature.getName())) { xarch.set(objRef, feature.getName(), UIDGenerator.generateUID()); } break; case ELEMENT_SINGLE: Serializable s = xarch.get(objRef, feature.getName()); if (s instanceof ObjRef && feature.isReference() && oldNewRefs.containsKey(s)) { xarch.set(objRef, feature.getName(), oldNewRefs.get(s)); } break; case ELEMENT_MULTIPLE: for (Serializable m : xarch.getAll(objRef, feature.getName())) { if (m instanceof ObjRef && feature.isReference() && oldNewRefs.containsKey(m)) { xarch.remove(objRef, feature.getName(), m); xarch.add(objRef, feature.getName(), oldNewRefs.get(m)); } } break; } } } Multimap<String, ObjRef> featureToCopiedObjRefs = ArrayListMultimap.create(); /** * Clones and copies the set of ObjRefs so that they can be pasted into a document later. */ public void copy(IXArchADT xarch, Iterable<ObjRef> objRefs) { featureToCopiedObjRefs.clear(); Map<ObjRef, ObjRef> oldNewRefs = Maps.newHashMap(); for (ObjRef objRef : objRefs) { featureToCopiedObjRefs.put(xarch.getContainingFeatureName(objRef), clone(xarch, objRef, oldNewRefs)); } for (ObjRef objRef : oldNewRefs.values()) { updateReferences(xarch, objRef, oldNewRefs); } } /** * Pastes the copied ObjRefs into a document after cloning them and randomizing their IDs so that there are no ID * conflicts. */ public Collection<ObjRef> paste(IXArchADT xarch, ObjRef rootRef) { IXArchADTTypeMetadata rootType = xarch.getTypeMetadata(rootRef); Map<ObjRef, ObjRef> oldNewRefs = Maps.newHashMap(); for (Entry<String, ObjRef> e : featureToCopiedObjRefs.entries()) { ObjRef objRef = clone(xarch, e.getValue(), oldNewRefs); randomizeIDs(xarch, objRef); IXArchADTFeature feature = rootType.getFeatures().get(e.getKey()); if (feature == null) { continue; } if (!xarch.isInstanceOf(objRef, feature.getNsURI(), feature.getTypeName())) { continue; } switch (feature.getType()) { case ATTRIBUTE: throw new IllegalArgumentException(); case ELEMENT_SINGLE: xarch.set(rootRef, feature.getName(), objRef); break; case ELEMENT_MULTIPLE: xarch.add(rootRef, feature.getName(), objRef); } } for (ObjRef objRef : oldNewRefs.values()) { updateReferences(xarch, objRef, oldNewRefs); } return oldNewRefs.values(); } }