Skip to content

Instantly share code, notes, and snippets.

@romualdrichard
Created January 7, 2026 14:19
Show Gist options
  • Select an option

  • Save romualdrichard/4bf78da96e3c6a8ec47a49316012309b to your computer and use it in GitHub Desktop.

Select an option

Save romualdrichard/4bf78da96e3c6a8ec47a49316012309b to your computer and use it in GitHub Desktop.
#jarchi create new model from views or directory of a model
/*
* jArchi Script: Export Selected Views with Tree Hierarchy
*
* This script displays a dialog with the complete folder and view hierarchy.
* You can check entire folders or individual views.
*
* Process:
* 1. Display tree with checkboxes
* 2. Save the current model with a new name (Save As)
* 3. Delete non-selected views
* 4. Delete concepts (elements + relationships) without visual representation
* 5. Delete empty view folders
* 6. Save and reload original
*
* IMPORTANT: The original model remains INTACT
*/
console.clear();
console.show();
// Import necessary Java classes
var Display = Java.type("org.eclipse.swt.widgets.Display");
var Shell = Java.type("org.eclipse.swt.widgets.Shell");
var Tree = Java.type("org.eclipse.swt.widgets.Tree");
var TreeItem = Java.type("org.eclipse.swt.widgets.TreeItem");
var Button = Java.type("org.eclipse.swt.widgets.Button");
var Label = Java.type("org.eclipse.swt.widgets.Label");
var Composite = Java.type("org.eclipse.swt.widgets.Composite");
var GridLayout = Java.type("org.eclipse.swt.layout.GridLayout");
var GridData = Java.type("org.eclipse.swt.layout.GridData");
var SWT = Java.type("org.eclipse.swt.SWT");
var SelectionAdapter = Java.type("org.eclipse.swt.events.SelectionAdapter");
// Global variables
var selectedViews = [];
var dialogResult = false;
var tree;
// Function to build the tree recursively
function buildTree(parentItem, folder) {
// Collect subfolders and sort them by name
var subfolders = [];
$(folder).children("folder").each(function(subfolder) {
subfolders.push(subfolder);
});
// Sort folders alphabetically
subfolders.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
// Add sorted subfolders
subfolders.forEach(function(subfolder) {
var folderItem;
if (parentItem) {
folderItem = new TreeItem(parentItem, SWT.NONE);
} else {
folderItem = new TreeItem(tree, SWT.NONE);
}
folderItem.setText("📁 " + subfolder.name);
folderItem.setData("type", "folder");
folderItem.setData("object", subfolder);
// Recursion for subfolders
buildTree(folderItem, subfolder);
});
// Collect views and sort them by name
var views = [];
$(folder).children("view").not("folder").each(function(view) {
views.push(view);
});
// Sort views alphabetically
views.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
// Add sorted views
views.forEach(function(view) {
var viewItem;
if (parentItem) {
viewItem = new TreeItem(parentItem, SWT.NONE);
} else {
viewItem = new TreeItem(tree, SWT.NONE);
}
viewItem.setText("📄 " + view.name);
viewItem.setData("type", "view");
viewItem.setData("object", view);
});
}
// Function to collect all checked views
function collectCheckedViews(items, checkedViews) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.getChecked()) {
var type = item.getData("type");
var obj = item.getData("object");
var isGrayed = item.getGrayed();
console.log(" Checked item: " + item.getText() + " (type: " + type + ", grayed: " + isGrayed + ")");
if (type === "view") {
// A view is checked, always add it
checkedViews.push(obj);
console.log(" → View added: " + obj.name + " (ID: " + obj.id + ")");
} else if (type === "folder" && !isGrayed) {
// A folder is checked AND NOT grayed = all its views are selected
console.log(" → Folder fully checked, collecting views...");
collectViewsFromFolder(obj, checkedViews);
} else if (type === "folder" && isGrayed) {
// A grayed folder = only some children checked
// We need to go down the tree to find which ones
console.log(" → Folder partially checked (grayed), descending to children...");
// Don't collect all views, continue recursion
}
}
// Always traverse children to find individually checked views
if (item.getItemCount() > 0) {
collectCheckedViews(item.getItems(), checkedViews);
}
}
}
// Function to collect all views from a folder
function collectViewsFromFolder(folder, viewsList) {
console.log(" Collecting from folder: " + folder.name);
var directViews = $(folder).children("view").not("folder");
console.log(" → " + directViews.size() + " direct views");
directViews.each(function(view) {
viewsList.push(view);
});
var subfolders = $(folder).children("folder");
console.log(" → " + subfolders.size() + " subfolders");
subfolders.each(function(subfolder) {
collectViewsFromFolder(subfolder, viewsList);
});
}
// Function to update checkbox state
function updateCheckState(item, checked) {
item.setChecked(checked);
item.setGrayed(false);
var children = item.getItems();
for (var i = 0; i < children.length; i++) {
updateCheckState(children[i], checked);
}
var parent = item.getParentItem();
if (parent) {
updateParentCheckState(parent);
}
}
// Function to update parent state
function updateParentCheckState(parent) {
var children = parent.getItems();
var checkedCount = 0;
var grayedCount = 0;
for (var i = 0; i < children.length; i++) {
if (children[i].getChecked()) checkedCount++;
if (children[i].getGrayed()) grayedCount++;
}
if (checkedCount === 0 && grayedCount === 0) {
parent.setChecked(false);
parent.setGrayed(false);
} else if (checkedCount === children.length) {
parent.setChecked(true);
parent.setGrayed(false);
} else {
parent.setChecked(true);
parent.setGrayed(true);
}
var grandParent = parent.getParentItem();
if (grandParent) {
updateParentCheckState(grandParent);
}
}
// Function to create the selection dialog
function createSelectionDialog() {
var display = Display.getCurrent() || Display.getDefault();
var shell = new Shell(display, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
shell.setText("Select Views to Export");
shell.setSize(700, 500);
var layout = new GridLayout(1, false);
layout.marginWidth = 10;
layout.marginHeight = 10;
shell.setLayout(layout);
var label = new Label(shell, SWT.NONE);
label.setText("Select views to export (check folders or individual views):");
var labelData = new GridData(SWT.FILL, SWT.CENTER, true, false);
label.setLayoutData(labelData);
tree = new Tree(shell, SWT.CHECK | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
var treeData = new GridData(SWT.FILL, SWT.FILL, true, true);
tree.setLayoutData(treeData);
// Find the Views folder
var viewsFolders = $("folder").filter(function(f) { return f.name === "Views"; });
if (viewsFolders.size() > 0) {
buildTree(null, viewsFolders.first());
} else {
// Add all views at root level
$("view").not("folder").each(function(view) {
var viewItem = new TreeItem(tree, SWT.NONE);
viewItem.setText("📄 " + view.name);
viewItem.setData("type", "view");
viewItem.setData("object", view);
});
}
tree.addListener(SWT.Selection, function(event) {
if (event.detail === SWT.CHECK) {
var item = event.item;
updateCheckState(item, item.getChecked());
}
});
var buttonComposite = new Composite(shell, SWT.NONE);
var buttonLayout = new GridLayout(5, false);
buttonComposite.setLayout(buttonLayout);
var buttonCompData = new GridData(SWT.FILL, SWT.CENTER, true, false);
buttonComposite.setLayoutData(buttonCompData);
var expandButton = new Button(buttonComposite, SWT.PUSH);
expandButton.setText("Expand All");
expandButton.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
expandAll(tree.getItems(), true);
}
});
var collapseButton = new Button(buttonComposite, SWT.PUSH);
collapseButton.setText("Collapse All");
collapseButton.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
expandAll(tree.getItems(), false);
}
});
var selectAllButton = new Button(buttonComposite, SWT.PUSH);
selectAllButton.setText("Select All");
selectAllButton.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var items = tree.getItems();
for (var i = 0; i < items.length; i++) {
updateCheckState(items[i], true);
}
}
});
var spacer = new Label(buttonComposite, SWT.NONE);
var spacerData = new GridData(SWT.FILL, SWT.CENTER, true, false);
spacer.setLayoutData(spacerData);
var okButton = new Button(buttonComposite, SWT.PUSH);
okButton.setText("Export");
var okButtonData = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
okButtonData.widthHint = 100;
okButton.setLayoutData(okButtonData);
okButton.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
selectedViews = [];
collectCheckedViews(tree.getItems(), selectedViews);
if (selectedViews.length === 0) {
window.alert("No views selected.\n\nPlease check at least one view or folder.");
return;
}
dialogResult = true;
shell.close();
}
});
var cancelButton = new Button(buttonComposite, SWT.PUSH);
cancelButton.setText("Cancel");
var cancelButtonData = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
cancelButtonData.widthHint = 100;
cancelButton.setLayoutData(cancelButtonData);
cancelButton.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
dialogResult = false;
shell.close();
}
});
shell.setDefaultButton(okButton);
// Expand first level
var items = tree.getItems();
for (var i = 0; i < items.length; i++) {
items[i].setExpanded(true);
}
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
return dialogResult;
}
// Function to expand/collapse all items
function expandAll(items, expand) {
for (var i = 0; i < items.length; i++) {
items[i].setExpanded(expand);
if (items[i].getItemCount() > 0) {
expandAll(items[i].getItems(), expand);
}
}
}
// Function to recursively delete empty folders
function deleteEmptyFolders(folder) {
var children = $(folder).children();
var hasContent = false;
// First, recursively process subfolders
$(folder).children("folder").each(function(subfolder) {
if (deleteEmptyFolders(subfolder)) {
hasContent = true;
}
});
// Check if there are views
if ($(folder).children("view").not("folder").size() > 0) {
hasContent = true;
}
// If folder is empty, delete it
if (!hasContent) {
console.log(" ✗ Empty folder deleted: " + folder.name);
folder.delete();
return false;
}
return true;
}
// Main program
try {
console.log("=============================================");
console.log(" Export Views with Tree Hierarchy");
console.log("=============================================\n");
// Check if model is set
if (!model.isSet()) {
console.log("Model is not selected!");
window.alert("No model is selected.\n\nPlease open an Archi model.");
exit();
}
console.log("Current model: " + model.name);
var sourcePath = model.getPath();
if (!sourcePath) {
window.alert(
"The current model has not been saved yet.\n\n" +
"Please save your model first (File → Save),\n" +
"then run the script again."
);
exit();
}
console.log("Source file: " + sourcePath);
// Get all views
var allViews = [];
$("view").not("folder").each(function(view) {
allViews.push(view);
});
if (allViews.length === 0) {
window.alert("No views available in the model.");
exit();
}
console.log("Number of available views: " + allViews.length);
// Display selection dialog
console.log("\n--- View Selection ---");
var result = createSelectionDialog();
if (!result || selectedViews.length === 0) {
console.log("Operation cancelled.");
exit();
}
// Deduplicate selected views
var uniqueViews = [];
var viewIds = {};
var selectedViewIds = [];
selectedViews.forEach(function(view) {
if (!viewIds[view.id]) {
viewIds[view.id] = true;
uniqueViews.push(view);
selectedViewIds.push(view.id);
}
});
selectedViews = uniqueViews;
console.log("\n" + selectedViews.length + " view(s) selected after deduplication:");
selectedViews.forEach(function(v) {
console.log(" ✓ " + v.name + " (ID: " + v.id + ")");
});
// Ask for confirmation
var confirmMsg = "You have selected " + selectedViews.length + " view(s) out of " + allViews.length + ".\n\n" +
"The script will:\n" +
"1. Save your model with a new name\n" +
"2. Delete non-selected views\n" +
"3. Delete unused concepts\n" +
"4. Delete empty folders\n\n" +
"Your current model will remain INTACT.\n\n" +
"Continue?";
var confirm = window.confirm(confirmMsg);
if (!confirm) {
console.log("\nOperation cancelled.");
exit();
}
// Ask for save path
console.log("\n--- Choose Destination File ---");
var savePath = window.promptSaveFile({
title: "Save New Model",
filterExtensions: ["*.archimate"],
fileName: "Export_" + model.name + ".archimate"
});
if (!savePath) {
console.log("Save cancelled.");
exit();
}
console.log("Destination file: " + savePath);
// Save current model with new name (equivalent to "Save As")
console.log("\n--- Saving Model to New File (Save As) ---");
model.save(savePath);
console.log("✓ Model saved as: " + savePath);
console.log("✓ Model remains open and active");
// Now "model" points to the same model, but saved under the new name
// STEP 1: Delete non-selected views
console.log("\n--- Step 1: Deleting Non-Selected Views ---");
var viewsToDelete = [];
$("view").not("folder").each(function(view) {
var isSelected = false;
for (var i = 0; i < selectedViewIds.length; i++) {
if (selectedViewIds[i] === view.id) {
isSelected = true;
break;
}
}
if (!isSelected) {
viewsToDelete.push(view);
}
});
console.log("Deleting " + viewsToDelete.length + " views...");
viewsToDelete.forEach(function(view) {
console.log(" ✗ " + view.name);
view.delete();
});
// STEP 2: Delete all concepts (elements + relationships) without visual representation
console.log("\n--- Step 2: Deleting Concepts Without Representation ---");
var allConcepts = $("concept");
var totalConcepts = allConcepts.size();
console.log("Total concepts to process: " + totalConcepts);
var processed = 0;
var deleted = 0;
var lastPercent = 0;
allConcepts.each(function(c) {
if ($(c).objectRefs().isEmpty()) {
c.delete();
deleted++;
}
processed++;
// Show progress as percentage every 5%
var percent = Math.floor((processed / totalConcepts) * 100);
if (percent >= lastPercent + 5 || processed === totalConcepts) {
console.log(" Progress: " + percent + "% (" + processed + "/" + totalConcepts + ") - Deleted so far: " + deleted);
lastPercent = percent;
}
});
console.log("✓ Completed: " + deleted + " concepts deleted out of " + totalConcepts);
var deletedCount = deleted;
// STEP 3: Delete empty folders
console.log("\n--- Step 3: Deleting Empty Folders ---");
var viewsFolders = $("folder").filter(function(f) { return f.name === "Views"; });
if (viewsFolders.size() > 0) {
deleteEmptyFolders(viewsFolders.first());
}
// Count remaining elements
var finalElementCount = $("element").size();
var finalRelCount = $("relationship").size();
var finalViewCount = $("view").not("folder").size();
// Save the new model
console.log("\n--- Saving New Model ---");
model.save(); // model now points to the new model
console.log("✓ New model saved");
// Reload original model
console.log("\n--- Reloading Original Model ---");
var originalModel = $.model.load(sourcePath);
originalModel.setAsCurrent();
console.log("✓ Original model reloaded and set as current");
// Summary
console.log("\n=============================================");
console.log(" ✓ EXPORT COMPLETED SUCCESSFULLY!");
console.log("=============================================");
console.log("\nFile created: " + savePath);
console.log("\nNew model contents:");
console.log(" • Views: " + finalViewCount);
console.log(" • Elements: " + finalElementCount);
console.log(" • Relationships: " + finalRelCount);
console.log("\nDeleted:");
console.log(" • Views: " + viewsToDelete.length);
console.log(" • Concepts: " + deletedCount);
console.log("\n✅ Your original model is intact and still open.");
console.log("\nTo open the new model:");
console.log("File → Open → " + savePath);
var successMsg = "✓ EXPORT COMPLETED SUCCESSFULLY!\n\n" +
"File created:\n" + savePath + "\n\n" +
"Contents:\n" +
" • Views: " + finalViewCount + "\n" +
" • Elements: " + finalElementCount + "\n" +
" • Relationships: " + finalRelCount + "\n\n" +
"✅ Your original model is intact\n" +
" and still open.\n\n" +
"To open the new model:\n" +
"File → Open → " + savePath;
window.alert(successMsg);
} catch(error) {
console.log("\n=============================================");
console.log(" ❌ ERROR");
console.log("=============================================");
console.log("\nError message:");
console.log(error);
if (error.stack) {
console.log("\nTechnical details:");
console.log(error.stack);
}
window.alert(
"❌ Error during export\n\n" +
error +
"\n\nCheck the jArchi console for details."
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment