Skip to content

Instantly share code, notes, and snippets.

@fanievh
Last active April 25, 2024 14:13
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fanievh/4ec7d14247616846f3d16b5e22dc80a9 to your computer and use it in GitHub Desktop.
Save fanievh/4ec7d14247616846f3d16b5e22dc80a9 to your computer and use it in GitHub Desktop.
Export Selected Folder in Source Model to New Target Model #jArchi
/*
* Export Selected Folder in Source Model to New Target Model
*
* https://gist.github.com/fanievh/4ec7d14247616846f3d16b5e22dc80a9
*
* This script copies a selected subset of a source model to a target model. The folder selected
* in the source model, will become the top level folder in the Views folder in the target model. Only
* elements, relationships, diagram objects, connections and images on any of the views in the selected
* folder in the source model, will be created in the target model.
*
* The script has the following major steps:
* 1. Iterate through the selected folder in the source model and create this folder, sub-folders and views
* in the target model.
* 2. Collect all the elements and relationships into 2 arrays: elements and relationships. Do this to ensure
* that no duplicates are in the 2 arrays. The object.id is used as the unique key.
* 3. Create the elements in the target model by iterating through the elements array.
* 4. Create the relationships in the target model by iterating through the relationships array.
* 5. Create the diagram objects in the target model by iterating through the diagram objects in the source
* model.
* 6. Create the diagram connections in the target model by iterating through the diagram connections in
* the source model.
* 7. Copy the custom images in the source model to a temporary directory (___DIR___/images) and upload and
* set the image property on the relevant diagram objects in the target model.
*
* Current known limitations:
* - The focus of the script is on Archimate and not Canvas and Sketch Views.
* - Doesn't support duplicate properties on elements.
* - Assumes that when custom images are in the source model, the source model is a zip file.
* - Assumes that views use the Manual Connection Router i.e. not the Manhattan Router. For each source view
* that has the Connection Router set to Manhattan, the Connection Router property of the generated
* target view has to be manually changed to Manhattan. The current version of jArchi (1.5) doesn't support
* changing this property programatically.
*
* - v1.0 - Initial version
* - v1.1 - Changed the way how source and target diagram objects are selected when creating diagram
* connections. v1.0 didn't work correctly when the same element appeared multiple times
* on a diagram.
* - v1.2 - Added code to copy custom images from the source model to the target model and some code
* refactoring
* - v1.2a - Bug fixes, added code to deal with relationships connecting to relationships because the
* original code didn't consider this. It won't handle all the cases. For now just write
* messages to the console when this occurs. The future solution is to reorder the sequence
* in which relationships and diagram connections are created to deal with dependencies.
* - v1.3 - Use topological sort to order the sequence in which relationships and diagram connections
* are created. Added view references. Bug fixes.
* - v1.3a - Added check if selection is a valid folder in Views.
* - v1.3b - Bug fix to copy images set in Diagram Notes. (Bug identified and fix proposed by
* https://gist.github.com/romualdrichard)
* - v1.3c - Added copying of iconColor property.
* - v1.3d - Bug fix how unordered and ordered arrays for relationships and diagram
* connections are combined.
* - v1.3e - Bug fix to use topological sort for any relationship / diagram connection where the source
* or target is any type of relationship and not just diagram-model-connection (const connectionTypes):
* https://github.com/archimatetool/archi-scripting-plugin/wiki/jArchi-Collection#object-selector-type
*
* Leveraged various community developed scripts as input.
* Use script https://gist.github.com/shinout/1232505 from Shin Suzuki for topological sort of
* relationships and diagram connections
*
* Author: Fanie van Heerden, 2023-2024
*
*/
let sourceModel = null;
let targetModel = null;
let folderMapping = [];
let viewMapping = [];
let elements = [];
let elementMapping = [];
let relationships = [];
let relationshipMapping = [];
let diagramObjectsMapping = [];
let customImages = [];
let objectToImage = new Map();
const ZipFile = Java.type("java.util.zip.ZipFile");
const InputStream = Java.type("java.io.InputStream");
const File = Java.type("java.io.File");
const FileOutputStream = Java.type("java.io.FileOutputStream");
const connectionTypes = new Set(["composition-relationship", "aggregation-relationship",
"assignment-relationship", "realization-relationship", "serving-relationship",
"access-relationship", "influence-relationship", "triggering-relationship",
"flow-relationship", "specialization-relationship", "association-relationship"],
"diagram-model-connection");
// set to true for verbose console messages or false for limited console messages
const debug = false;
// Show progress in the console for longer running steps
class Progress {
constructor(numItems, percentInc) {
this.counter = 0;
this.percentInc = percentInc;
this.showEvery = Math.ceil(numItems * percentInc / 100);
}
showProgress() {
this.counter++;
if (this.counter % this.showEvery === 0) {
let inc = this.counter / this.showEvery;
let str = "*".repeat(inc) + " " + inc * this.percentInc +"%";
console.log(str);
}
}
}
// function checks if the selection is the Views folder or a sub-folder
// of Views
function isValidSelection(item)
{
if (item.type !== "folder") {
console.log("Selection has to be a folder in Views")
return false;
}
if (item.name === "Views") {
return true;
}
let parent = $(item).parents().filter(".Views");
if (parent.size() > 0) {
return true;
} else {
console.log("\"" + item.name + "\" is not a valid selection");
return false;
}
}
function CleanFileName(theString) {
let regex = /[\[\]\(\)\#\\\/\"]/gi;
return theString.replace(regex,"")
.replaceAll(" ","-");
}
/**
* general topological sort
* @author SHIN Suzuki (shinout310@gmail.com)
* @param Array<Array> edges : list of edges. each edge forms Array<ID,ID> e.g. [12 , 3]
*
* @returns Array : topological sorted list of IDs
**/
function tsort(edges) {
var nodes = {}, // hash: stringified id of the node => { id: id, afters: lisf of ids }
sorted = [], // sorted list of IDs ( returned value )
visited = {}; // hash: id of already visited node => true
var Node = function(id) {
this.id = id;
this.afters = [];
}
// 1. build data structures
edges.forEach(function(v) {
var from = v[0], to = v[1];
if (!nodes[from]) nodes[from] = new Node(from);
if (!nodes[to]) nodes[to] = new Node(to);
nodes[from].afters.push(to);
});
// 2. topological sort
Object.keys(nodes).forEach(function visit(idstr, ancestors) {
var node = nodes[idstr],
id = node.id;
// if already exists, do nothing
if (visited[idstr]) return;
if (!Array.isArray(ancestors)) ancestors = [];
ancestors.push(id);
visited[idstr] = true;
node.afters.forEach(function(afterID) {
if (ancestors.indexOf(afterID) >= 0) // if already in ancestors, a closed chain exists.
throw new Error('closed chain : ' + afterID + ' is in ' + id);
visit(afterID.toString(), ancestors.map(function(v) { return v })); // recursive call
});
sorted.unshift(id);
});
return sorted;
}
// copy the properties from an object in the source model to an object in the target model
function copyProperties(sourceObject, targetObject) {
let properties = sourceObject.prop();
for (let j = 0; j < properties.length; j++){
if (debug) {console.log("Set property = " + properties[j]);}
targetObject.prop(properties[j], sourceObject.prop(properties[j], false), false);
}
}
// create and copy a specialization from an object in the source model to an object in the target model
function copySpecialization(sourceObject, targetObject) {
let specializationName = sourceObject.specialization;
if (specializationName && specializationName.length > 0) {
let sourceSpecialization = sourceModel.findSpecialization(specializationName, sourceObject.type);
let image = sourceSpecialization.image;
if (image) {
let imagePath = image.get("path");
customImages[imagePath] = null;
}
let targetSpecialization = targetModel.findSpecialization(sourceSpecialization.name, sourceSpecialization.type);
if (targetSpecialization) {
if (debug) {console.log("Set object specialization to: " + targetSpecialization.name);}
targetObject.specialization = targetSpecialization.name;
} else {
if (debug) {console.log("Create Specialization Name = " + sourceSpecialization.name + " ; Type = " + sourceSpecialization.type);}
targetSpecialization = targetModel.createSpecialization(sourceSpecialization.name, sourceSpecialization.type);
if (debug) {console.log("Set object specialization to: " + targetSpecialization.name);}
targetObject.specialization = targetSpecialization.name;
}
}
}
// recursively iterates through all the child objects of a view in the source model and add the elements to
// an array "elements" and the relationships to an array "relationships". These 2 arrays are then used in later
// functions to create the corresponding elements and relationshps respectively in the target model
function nestedObjects(object) {
$(object).children().not("relationship").each(function(obj) {
// get the concept from the visual object
let concept = obj.concept;
// add the concept to the elements array if it hasn't yet been added
if (concept && !elements[concept.id]) {
elements[concept.id] = concept;
}
if ($(obj).children().length>0) {
nestedObjects(obj);
}
});
$(object).children("relationship").each(function(obj) {
// get the relationship from the object
let relationship = obj.concept;
if (relationship && !relationships[relationship.id]) {
relationships[relationship.id] = relationship;
}
});
}
// this is the first top level function that iterates through the selected folder and sub-folders and views in
// the source model and creates the corresponding folder, sub-folders and views in the target model
function processFolder(item, targetParentFolder)
{
let newFolder = null;
if (item.type == "folder") {
if (debug) {console.log("Parent Folder: " + targetParentFolder.name);}
// Create new child folder in target model
console.log("Create Folder: " + item.name);
newFolder = targetParentFolder.createFolder(item.name);
newFolder.documentation = item.documentation;
if (item.labelExpression) {
newFolder.labelExpression = item.labelExpression;
}
copyProperties(item, newFolder);
folderMapping[item.id] = newFolder;
}
$(item).children().each(function(child){
// if the child is a folder, call the function recursively until a view is found
if(child.type == "folder")
{
processFolder(child, newFolder);
}
// found a view
if(child.type == "archimate-diagram-model")
{
console.log("Create View: " + child.name);
// Create new view in target model
let newView = targetModel.createArchimateView(child.name, newFolder);
viewMapping[child.id] = newView;
newView.documentation = child.documentation;
newView.viewpoint = child.viewpoint.name;
copyProperties(child, newView);
nestedObjects(child);
}
});
}
// this function iterates through the previously collected elements in the source model and
// creates the corresponding elements in the target model
function createElements() {
let numItems = Object.keys(elements).length;
console.log("Create " + numItems + " Elements in the Target Model");
let progress = new Progress(numItems, 20);
Object.entries(elements).forEach(elementObj => {
// get the concept
let e = elementObj[1];
let name = e.name;
let type = e.type;
let documentation =e.documentation;
let specializationName = e.specialization;
let junctionType = e.junctionType;
// "diagram-model-group" and "diagram-model-note" are visual objects and only created in a later step
// in the target model
if (type !== "diagram-model-group" && type !== "diagram-model-note") {
if (debug) {console.log("Create Element: " + name + " ; Type: " + type);}
// create the element in the target model
let newElement = targetModel.createElement(type, name);
newElement.documentation = documentation;
// if element is a junction
if (junctionType) {
newElement.junctionType = junctionType;
}
// check if a specialization should be set for the element and create/set it
copySpecialization(e, newElement);
// set properties
copyProperties(e, newElement);
// create a map between the concept id in the source model and the new element object in the target model to use for further processing
elementMapping[e.id] = newElement;
}
progress.showProgress();
});
}
// this function takes a relationship object in source model as input and
// creates the corresponding relationship in the target model
function createRelationship(rel) {
let relID = rel.id;
let name = rel.name;
let type = rel.type;
let source = elementMapping[rel.source.id];
if (source === undefined) {
source = relationshipMapping[rel.source.id];
if (source === undefined) {
console.log("*** Relationship source for relationship " + rel + " not found");
return;
}
}
let target = elementMapping[rel.target.id];
if (target === undefined) {
target = relationshipMapping[rel.target.id];
if (target === undefined) {
console.log("*** Relationship target for relationship " + rel + " not found");
return;
}
}
let documentation = rel.documentation;
let specializationName = rel.specialization;
if (debug) {console.log("Create Relationship: " + name + " ; Type: " + type + " ; Source: " + source.name + " ; Target: " + target.name);}
// create the relationship in the target model
try {
let newRelationship = targetModel.createRelationship(type, name, source, target);
// create a map between the concept id in the source model and the new relationship object in the target
// model to use for further processing
relationshipMapping[relID] = newRelationship;
newRelationship.documentation = documentation;
// set specialization
copySpecialization(rel, newRelationship);
// set properties
copyProperties(rel, newRelationship);
if (rel.accessType) {
newRelationship.accessType = rel.accessType;
}
if (rel.associationDirected) {
newRelationship.associationDirected = rel.associationDirected;
}
if (rel.influenceStrength) {
newRelationship.influenceStrength = rel.influenceStrength;
}
} catch (err) {
let message = "!!! Couldn't create relationship " + name + " of type " + type;
if (source) message += " with source " + source;
if (target) message += " with target " + target;
console.log(message);
console.log(err.message);
}
}
// this function iterates through the previously collected relationships in the source model and
// creates the corresponding relationships in the target model
function createRelationships() {
let edges = [];
let unorderedRelationships = [];
// create 2 arrays, one with relationships that don't have a source or target pointing to a
// relationship i.e. elements only and the other array of edges for relationhips that do have a source
// or target pointing to a relationship. The latter is used to sort these relationships
// topologically to ensure relationships are created in the right order ito dependencies
Object.entries(relationships).forEach(relObj => {
let obj = relObj[1];
let relSource = obj.source;
let relTarget = obj.target;
if (relSource.type.includes("relationship")) {
if (debug) console.log("*** Relationship with source that is a relationship");
edges.push([relSource.id, obj.id]);
} else if (relTarget.type.includes("relationship")) {
if (debug) console.log("*** Relationship with target that is a relationship:");
edges.push([relTarget.id, obj.id]);
} else {
unorderedRelationships.push(relObj[0]);
}
});
// topological sort
let ts = tsort(edges);
let orderedRelationships = new Set(unorderedRelationships);
// add the 2 arrays above together in a single ordered Set
for (j = 0; j < ts.length; j++) {
orderedRelationships.add(ts[j]);
}
let numItems = orderedRelationships.size;
console.log("Create " + numItems + " Relationships in the Target Model");
let progress = new Progress(numItems, 20);
// iterate through the ordered relationhips and create them
for (let id of orderedRelationships) {
let rel = relationships[id];
if (rel) createRelationship(rel);
progress.showProgress();
}
}
// this function copies various visual properties from a diagram object in the source model
// to a diagram object in the target model
function copyVisualObjectProperties(sourceObject, targetObject) {
targetObject.name = sourceObject.name;
targetObject.figureType = sourceObject.figureType;
targetObject.showIcon = sourceObject.showIcon;
targetObject.fontName = sourceObject.fontName;
targetObject.fontSize = sourceObject.fontSize;
targetObject.fontStyle = sourceObject.fontStyle;
targetObject.fontColor = sourceObject.fontColor;
targetObject.textAlignment = sourceObject.textAlignment;
targetObject.textPosition = sourceObject.textPosition;
targetObject.fillColor = sourceObject.fillColor;
targetObject.opacity = sourceObject.opacity;
targetObject.outlineOpacity = sourceObject.outlineOpacity;
targetObject.gradient = sourceObject.gradient;
targetObject.lineColor = sourceObject.lineColor;
targetObject.imagePosition = sourceObject.imagePosition;
targetObject.imageSource = sourceObject.imageSource;
try {
// only supported from jArchi 1.5
targetObject.iconColor = sourceObject.iconColor;
} catch (err) {
}
if (sourceObject.labelExpression) {
targetObject.labelExpression = sourceObject.labelExpression;
}
let image = sourceObject.image;
// save the image path of the custom image into the customImages map
if (image) {
if (debug) {console.log("Source Object Image: " + image);}
let imagePath = image.get("path");
customImages[imagePath] = null;
objectToImage.set(targetObject, imagePath);
}
}
// this function copies various visual properties from a diagram connection in the source model
// to a diagram connection in the target model
function copyVisualConnectionProperties(sourceConnection, targetConnection) {
targetConnection.labelVisible = sourceConnection.labelVisible;
targetConnection.lineWidth = sourceConnection.lineWidth;
targetConnection.textAlignment = sourceConnection.textAlignment;
targetConnection.textPosition = sourceConnection.textPosition;
}
// this function creates a corresponding diagram object in the target model from a diagram
// object in the source model
function createDiagramObjectNested(sourceObject, targetObject) {
try {
let type = sourceObject.type;
let x = sourceObject.bounds.x;
let y = sourceObject.bounds.y;
let width = sourceObject.bounds.width;
let height = sourceObject.bounds.height;
let newVisualObject = null;
if (debug) {console.log("Diagram Object: " + sourceObject.name + " ; Type: " + type);}
switch (type) {
case "diagram-model-group":
case "group":
newVisualObject = targetObject.createObject(type, x, y, width, height);
newVisualObject.name = sourceObject.name;
diagramObjectsMapping[sourceObject.id] = newVisualObject;
copyVisualObjectProperties(sourceObject, newVisualObject);
newVisualObject.borderType = sourceObject.borderType;
$(sourceObject).children().not("relationship").each(function(child){
createDiagramObjectNested(child, newVisualObject);
});
break;
case "diagram-model-note":
case "note":
newVisualObject = targetObject.createObject(type, x, y, width, height);
newVisualObject.name = sourceObject.name;
diagramObjectsMapping[sourceObject.id] = newVisualObject;
copyVisualObjectProperties(sourceObject, newVisualObject);
newVisualObject.borderType = sourceObject.borderType;
newVisualObject.setText(sourceObject.text);
break;
case "archimate-diagram-model":
let targetView = viewMapping[sourceObject.refView.id];
let viewRef = targetObject.createViewReference(targetView, x, y, width, height);
diagramObjectsMapping[sourceObject.id] = viewRef;
break;
default:
let sourceConcept = sourceObject.concept;
let targetConcept = elementMapping[sourceConcept.id];
newVisualObject = targetObject.add(targetConcept, x, y, width, height);
diagramObjectsMapping[sourceObject.id] = newVisualObject;
copyVisualObjectProperties(sourceObject, newVisualObject);
$(sourceObject).children().not("relationship").each(function(child){
createDiagramObjectNested(child, newVisualObject);
});
}
} catch (err) {
console.log("!!! Couldn't create Diagram Object for " + sourceObject + " for concept " + sourceObject.concept);
console.log(err.message);
}
}
// this function iterates through the previously collected views in the source model and creates
// the corresponding diagram objects (excluding relationships) in the target model
function createDiagramObjects()
{
let numItems = Object.keys(viewMapping).length;
console.log("Iterate through " + numItems + " Views in the Target Model and add Diagram Objects");
let progress = new Progress(numItems, 20);
Object.entries(viewMapping).forEach(mapObj => {
let sourceView = $("#" + mapObj[0]).first();
let targetView = mapObj[1];
if (debug) {console.log("Source View: " + sourceView.name + " ; Target View: " + targetView.name);}
$(sourceView).children().not("relationship").not("diagram-model-connection").each(function(child){
createDiagramObjectNested(child, targetView);
});
progress.showProgress();
});
}
// this function creates a corresponding diagram relationship in the target model from a diagram
// relationship in the source model
function createDiagramConnection(sourceConnection, targetView) {
try {
let relSourceDiagramObject = diagramObjectsMapping[sourceConnection.source.id];
let relTargetDiagramObject = diagramObjectsMapping[sourceConnection.target.id];
let sourceConcept = sourceConnection.concept;
let targetConcept = relationshipMapping[sourceConcept.id];
if (targetConcept === undefined) {
let message = "!!! Couldn't create Diagram Connection for " + sourceConnection;
if (relSourceDiagramObject) message += " from " + relSourceDiagramObject;
if (relTargetDiagramObject) message += " to " + relTargetDiagramObject;
console.log(message);
return;
}
if (debug) console.log("targetConcept: " + targetConcept + " ; sourceDiagramObject: " + relSourceDiagramObject + " ; targetDiagramObject: " + relTargetDiagramObject);
let newDiagramConnection = targetView.add(targetConcept, relSourceDiagramObject, relTargetDiagramObject);
// add new relationship to diagramObjectsMapping so that relationships still to be created, which might
// connect to a relationship as source or target, will find a visual object
diagramObjectsMapping[sourceConnection.id] = newDiagramConnection;
copyVisualObjectProperties(sourceConnection, newDiagramConnection);
copyVisualConnectionProperties(sourceConnection, newDiagramConnection);
for (j = 0; j < sourceConnection.relativeBendpoints.length; j++) {
let bp = sourceConnection.relativeBendpoints[j];
if (debug) console.log("bp; " + bp + " j: " + j);
newDiagramConnection.addRelativeBendpoint(bp, j);
}
} catch (err) {
console.log("\n" + err.message);
}
}
// this function iterates through the previously collected views in the source model and creates
// the corresponding diagram relationships in the target model
function createDiagramConnections() {
let numItems = Object.keys(viewMapping).length;
console.log("Iterate through " + numItems + " Views in the Target Model and add Diagram Connections");
let progress = new Progress(numItems, 20);
Object.entries(viewMapping).forEach(mapObj => {
let sourceView = $("#" + mapObj[0]).first();
let targetView = mapObj[1];
if (debug) {console.log("\nSource View: " + sourceView.name + " ; Target View: " + targetView.name);}
let edges = [];
let unorderedConnections = [];
let diagramConnections = [];
$(sourceView).children("relationship").each(function(child){
diagramConnections[child.id] = child;
let connectionSource = child.source;
let connectionTarget = child.target;
if (connectionTypes.has(connectionSource.type) || connectionTypes.has(connectionTarget.type)) {
if (connectionTypes.has(connectionSource.type)) {
if (debug) console.log("*** Diagram Connection with source that is a Diagram Connection");
edges.push([connectionSource.id, child.id]);
}
if (connectionTypes.has(connectionTarget.type)) {
if (debug) console.log("*** Diagram Connection with target that is a Diagram Connection");
edges.push([connectionTarget.id, child.id]);
}
} else {
unorderedConnections.push(child.id);
}
});
// topological sort
let ts = tsort(edges);
let orderedConnections = new Set(unorderedConnections);
// add the 2 arrays above together in a single ordered Set
for (j = 0; j < ts.length; j++) {
orderedConnections.add(ts[j]);
}
// iterate through the ordered Diagram Connections and create them in the target model
for (let id of orderedConnections) {
let sourceModelConnection = diagramConnections[id];
if (sourceModelConnection) createDiagramConnection(sourceModelConnection, targetView);
}
progress.showProgress();
});
}
// this function iterates through the previously collected custom images in the source model, saves them to a
// a temporary folder and then adds them to the target model
function copyCustomImages() {
let sourceModelPath = sourceModel.getPath();
try {
let sourceFile = new ZipFile(sourceModelPath);
let imageDirectory = new File(__DIR__ + "/images");
let directoryCreated = false;
// create an images directory in the working directory to temporarily store the images from the
// source model
if (!imageDirectory.exists()) {
imageDirectory.mkdir();
directoryCreated = true;
}
let numItems = Object.keys(customImages).length;
console.log("Add " + numItems + " Custom Images to the Target Model");
let progress = new Progress(numItems, 20);
// extract the images from the source model zip file and save it to the images directory, upload
// it to the target model and delete the images from the images directory
Object.entries(customImages).forEach(mapObj => {
let imagePath = mapObj[0];
if (debug) console.log("Source Image Path: " + imagePath);
let zipEntry = sourceFile.getEntry(imagePath);
if (zipEntry) {
let inputStream = sourceFile.getInputStream(zipEntry);
let outputFile = new File(__DIR__ + "/" + imagePath);
if (debug) console.log("Custom Image File: " + outputFile.getPath());
outputFile.createNewFile();
let outputStream = new FileOutputStream(outputFile);
inputStream.transferTo(outputStream);
inputStream.close();
outputStream.close();
customImages[imagePath] = targetModel.createImage(outputFile.getPath());
outputFile.delete();
}
progress.showProgress();
});
sourceFile.close();
// delete the images directory if it was created in this script
if (directoryCreated) {
imageDirectory.delete();
}
// Iterate through the diagram objects and set the image field of the relevant diagram objects
numItems = objectToImage.size;
console.log("Iterate through " + numItems + " Diagram Objects in the Target Model and set Custom Image property");
progress = new Progress(numItems, 20);
objectToImage.forEach((value, key) => {
let targetDiagramObject = key;
let sourceImagePath = value;
let targetImage = customImages[sourceImagePath];
if (targetImage) {
targetDiagramObject.image = targetImage;
}
progress.showProgress();
});
// Iterate through the specializations in the target model and set the image to the relevant image
// in the target model
console.log("Add Custom Images to Specializations in the Target Model");
targetModel.specializations.forEach(specialization => {
if (debug) console.log("Specialization: " + specialization.name + " ; " + specialization.type);
let sourceSpecialization = sourceModel.findSpecialization(specialization.name, specialization.type);
let sourceImage = sourceSpecialization.image;
if (sourceImage) {
let imagePath = sourceImage.get("path");
let targetImage = customImages[imagePath];
specialization.image = targetImage;
}
});
}
catch (err) {
console.log("*** " + err.message);
return;
}
}
// this is the main script
console.show();
console.clear();
console.log("> Export start");
if ($(selection).filter("folder").size() == 1) {
let selectedFolder = $(selection).filter("folder").first();
if (!isValidSelection(selectedFolder)) {
window.alert("Not a valid selection. Please select a Folder containing the Views before running the script. Only one folder should be selected");
} else {
console.log("Selected Folder: " + selectedFolder.name);
sourceModel = selectedFolder.model;
console.log("Source Model: " + sourceModel.name);
var targetModelName = window.prompt("Please enter Export Model Name:", selectedFolder.name);
console.log("Target Model Name: ", targetModelName);
targetModel = $.model.create(targetModelName);
console.log("Create Target Model: " + targetModel.name);
targetModel.setAsCurrent();
let parentFolder = $("folder.Views").first();
sourceModel.setAsCurrent();
console.log("Create Folders and Views in Target Model")
processFolder(selectedFolder, parentFolder);
//Create Elements in Target Model
createElements();
//Create Relationships in Target Model
createRelationships();
// Create Diagram Objects in Target Model
createDiagramObjects();
// Create Diagram Connections in Target Model
createDiagramConnections();
// Add custom images to target model
copyCustomImages();
let targetModelFileName = CleanFileName(selectedFolder.name) + ".archimate";
targetModelFileName = window.promptSaveFile({ title: "Export Archi Model Filename", filterExtensions: [ "*.archimate" ], fileName: targetModelFileName } );
console.log("Saving Target Model Filename: " + targetModelFileName);
targetModel.save(targetModelFileName);
console.log("> Export done");
}
}
else
{
window.alert("No Folder selected. Please select a Folder containing the Views before running the script. Only one folder should be selected");
console.log("> Please select a Folder containing the Views. Only one Folder should be selected");
};
@rich-biker
Copy link

This is a great gist. Perfect for tidying up a messy model by removing chunks. Thankyou.

@romualdrichard
Copy link

Nice, I try on my model with a folder of views.
I am using image in notes for legend purpose, adding logo to applications...
Theses images are not propagated to the new model.
Is it possible to do this?

@romualdrichard
Copy link

romualdrichard commented Dec 6, 2023

Changing the block line 509 to 👍

try{
	if ((sourceObject.imageSource === IMAGE_SOURCE.CUSTOM ) || (sourceObject.getImage().path)) {
      
        if (debug) {console.log("Source Object Image: " + sourceObject.image);}
        targetObject.imageSource = IMAGE_SOURCE.CUSTOM;
        let image = sourceObject.image;

        // save the image path of the custom image into the customImages map
        if (image) {
            let imagePath = image.get("path");
            customImages[imagePath] = null;
            objectToImage.set(targetObject, imagePath);
        }
        
    }
	}catch(err) {if (debug) console.log("err path, ignore");};

Do the trick

@fanievh
Copy link
Author

fanievh commented Dec 6, 2023

Changing the block line 509 to 👍

try{
	if ((sourceObject.imageSource === IMAGE_SOURCE.CUSTOM ) || (sourceObject.getImage().path)) {
      
        if (debug) {console.log("Source Object Image: " + sourceObject.image);}
        targetObject.imageSource = IMAGE_SOURCE.CUSTOM;
        let image = sourceObject.image;

        // save the image path of the custom image into the customImages map
        if (image) {
            let imagePath = image.get("path");
            customImages[imagePath] = null;
            objectToImage.set(targetObject, imagePath);
        }
        
    }
	}catch(err) {if (debug) console.log("err path, ignore");};

Do the trick

Sorry for only responding now. Thank you for identifying the bug and the fix :-) I've refactored the code a bit and hopefully that works.

@fanievh
Copy link
Author

fanievh commented Dec 6, 2023

This is a great gist. Perfect for tidying up a messy model by removing chunks. Thankyou.

Thank you, glad it's useful :-)

@romualdrichard
Copy link

I've tried the last version and it's working well. Thanks

@fanievh
Copy link
Author

fanievh commented Dec 7, 2023

I've tried the last version and it's working well. Thanks

Thank you for trying it. I made another small change to copy the iconColor property. This is only supported from jArchi 1.5.

@romualdrichard
Copy link

Hi,
Happy New Year.
I have a little problem, in some of my views I have relationships between elements and relationships. The relationships are not in the new model It looks like relaitonships are not in th elementMAping, is it that? Anyway to change this?

Regards

@fanievh
Copy link
Author

fanievh commented Jan 3, 2024

Hi, Happy New Year. I have a little problem, in some of my views I have relationships between elements and relationships. The relationships are not in the new model It looks like relaitonships are not in th elementMAping, is it that? Anyway to change this?

Regards

Happy new year to you too. I did test relationships between elements and relationships but there might be scenarios I overlooked. I'll have a look. If you have a small sample model that exhibits the behaviour that you can share with me, that will help in debugging and fixing?

Regards,
Fanie

@romualdrichard
Copy link

Hi Fanie,

Capture d’écran 2024-01-03 095630

Here is an example, link to the flow relation is from the application to the flow but I think it should be the same from the relation to the application.

Regards,
Romuald

@fanievh
Copy link
Author

fanievh commented Jan 3, 2024

Hi Fanie,

Here is an example, link to the flow relation is from the application to the flow but I think it should be the same from the relation to the application.

Regards, Romuald

Hi Romuald, please try the latest version and let me know :-)

@romualdrichard
Copy link

Hi Fanie,

I tried it and it's working well. I have one exception and found that one relationship was not drawn. It's strange because I can find it in two views on the parent model and after export only in one view. I tried to reproduce this in a simple model but I cant. The exception said "Cannot invoke "com.archimatetool.script.dom.model.DiagramModelComponentProxy.isArchimateConcept()" because "target" is null" which is not true.

@fanievh
Copy link
Author

fanievh commented Jan 3, 2024

Hi Fanie,

I tried it and it's working well. I have one exception and found that one relationship was not drawn. It's strange because I can find it in two views on the parent model and after export only in one view. I tried to reproduce this in a simple model but I cant. The exception said "Cannot invoke "com.archimatetool.script.dom.model.DiagramModelComponentProxy.isArchimateConcept()" because "target" is null" which is not true.

If you can email me the actual Archi model I can debug it (assuming there's nothing in there that's sensitive). fanievh [at] icloud [dot] com.

@fanievh
Copy link
Author

fanievh commented Jan 5, 2024

Hi Fanie,

I tried it and it's working well. I have one exception and found that one relationship was not drawn. It's strange because I can find it in two views on the parent model and after export only in one view. I tried to reproduce this in a simple model but I cant. The exception said "Cannot invoke "com.archimatetool.script.dom.model.DiagramModelComponentProxy.isArchimateConcept()" because "target" is null" which is not true.

Hi Richard,

I've updated the script and hopefully it now works 100% for you. jArchi v1.5 doesn't support getting / setting the Connection Router property of views so the script assumes it's Manual and not Manhattan. You need to change it per target generated view if you need it to be Manhattan. Please let me know if there are more pesky bugs in your usage.

Regards, Fanie.

@romualdrichard
Copy link

Thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment