Skip to content

Instantly share code, notes, and snippets.

@romualdrichard
Last active January 8, 2026 13:39
Show Gist options
  • Select an option

  • Save romualdrichard/021185373284b30c26a0656932b86ff0 to your computer and use it in GitHub Desktop.

Select an option

Save romualdrichard/021185373284b30c26a0656932b86ff0 to your computer and use it in GitHub Desktop.
#jarchi Create View from Properties
/*
* jArchi Script: Create or update view based on properties (Elements and Relationships)
* Creates a new view or updates an existing view with elements and/or relationships matching property/value pairs
* Logic: OR between values of same property, AND between different properties
*/
console.clear();
console.show();
// Java imports for the GUI
var SWT = Java.type("org.eclipse.swt.SWT");
var GridLayout = Java.type("org.eclipse.swt.layout.GridLayout");
var GridData = Java.type("org.eclipse.swt.layout.GridData");
var Shell = Java.type("org.eclipse.swt.widgets.Shell");
var Display = Java.type("org.eclipse.swt.widgets.Display");
var Label = Java.type("org.eclipse.swt.widgets.Label");
var ListWidget = Java.type("org.eclipse.swt.widgets.List");
var Button = Java.type("org.eclipse.swt.widgets.Button");
var MessageBox = Java.type("org.eclipse.swt.widgets.MessageBox");
var Composite = Java.type("org.eclipse.swt.widgets.Composite");
var Group = Java.type("org.eclipse.swt.widgets.Group");
var Combo = Java.type("org.eclipse.swt.widgets.Combo");
/**
* Retrieves all unique properties with their values for elements or relationships
*/
function getAllPropertiesWithValues(targetType) {
var propsMap = {};
var itemCount = 0;
var propCount = 0;
console.log("Starting property analysis for " + targetType + "...");
$(targetType).each(function(item) {
itemCount++;
// Get all property names
var propNames = $(item).prop();
propNames.forEach(function(propName) {
propCount++;
// Get the value for this property name
var propValue = $(item).prop(propName);
if (!propsMap[propName]) {
propsMap[propName] = {};
}
// Use the value, even if empty
var val = propValue || "(empty)";
propsMap[propName][val] = true;
});
});
console.log("Items analyzed: " + itemCount);
console.log("Properties found: " + propCount);
console.log("Unique properties: " + Object.keys(propsMap).length);
// Display some examples
var propNames = Object.keys(propsMap);
for (var i = 0; i < Math.min(5, propNames.length); i++) {
var propName = propNames[i];
var values = Object.keys(propsMap[propName]);
console.log(" Property: [" + propName + "] -> " + values.length + " values");
}
return propsMap;
}
/**
* Gets all views in the model
*/
function getAllViews() {
var views = [];
$("view").each(function(view) {
views.push(view);
});
return views;
}
/**
* Organizes filters by property name
* Returns: { propName1: [value1, value2, ...], propName2: [value3, ...], ... }
*/
function organizeFiltersByProperty(selectedFilters) {
var filtersByProp = {};
for (var i = 0; i < selectedFilters.length; i++) {
var filter = selectedFilters[i];
if (!filtersByProp[filter.propName]) {
filtersByProp[filter.propName] = [];
}
filtersByProp[filter.propName].push(filter.propValue);
}
return filtersByProp;
}
/**
* Finds all elements having the specified property/value pairs
* Logic: OR between values of same property, AND between different properties
*/
function findElementsByProperties(selectedFilters) {
var elements = [];
var filtersByProp = organizeFiltersByProperty(selectedFilters);
$("element").each(function(element) {
var matchesAll = true;
// Check each property (AND logic between properties)
for (var propName in filtersByProp) {
var propValue = $(element).prop(propName);
var val = propValue || "(empty)";
var acceptedValues = filtersByProp[propName];
var matchesThisProperty = false;
// Check if element matches ANY of the values for this property (OR logic)
for (var i = 0; i < acceptedValues.length; i++) {
if (val === acceptedValues[i]) {
matchesThisProperty = true;
break;
}
}
if (!matchesThisProperty) {
matchesAll = false;
break;
}
}
if (matchesAll) {
elements.push(element);
}
});
return elements;
}
/**
* Finds all relationships having the specified property/value pairs
* Logic: OR between values of same property, AND between different properties
*/
function findRelationshipsByProperties(selectedFilters) {
var relationships = [];
var filtersByProp = organizeFiltersByProperty(selectedFilters);
$("relationship").each(function(relationship) {
var matchesAll = true;
// Check each property (AND logic between properties)
for (var propName in filtersByProp) {
var propValue = $(relationship).prop(propName);
var val = propValue || "(empty)";
var acceptedValues = filtersByProp[propName];
var matchesThisProperty = false;
// Check if relationship matches ANY of the values for this property (OR logic)
for (var i = 0; i < acceptedValues.length; i++) {
if (val === acceptedValues[i]) {
matchesThisProperty = true;
break;
}
}
if (!matchesThisProperty) {
matchesAll = false;
break;
}
}
if (matchesAll) {
relationships.push(relationship);
}
});
return relationships;
}
/**
* Creates a new view or updates an existing one with filtered elements and/or relationships
*/
function createOrUpdateView(elements, relationships, selectedFilters, targetType, existingView) {
if (elements.length === 0 && relationships.length === 0) {
var mb = new MessageBox(Display.getCurrent().getActiveShell(), SWT.ICON_WARNING | SWT.OK);
mb.setText("No Items Found");
mb.setMessage("No items found matching the selected criteria.");
mb.open();
return;
}
var view;
var isUpdate = (existingView !== null);
if (isUpdate) {
view = existingView;
console.log("Updating existing view: " + view.name);
} else {
// Create view name
var filtersByProp = organizeFiltersByProperty(selectedFilters);
var filterDesc = [];
for (var propName in filtersByProp) {
var values = filtersByProp[propName];
if (values.length === 1) {
filterDesc.push(propName + "=" + values[0]);
} else {
filterDesc.push(propName + " IN (" + values.join(", ") + ")");
}
}
var prefix = targetType === "element" ? "Elements" :
targetType === "relationship" ? "Relationships" : "Mixed";
var viewName = "Filtered " + prefix + " - " + filterDesc.join(" AND ");
// Limit name length
if (viewName.length > 150) {
viewName = viewName.substring(0, 147) + "...";
}
view = model.createArchimateView(viewName);
console.log("Creating new view: " + viewName);
}
// Get existing elements and relationships in the view (if updating)
var existingElementIds = {};
var existingRelationshipIds = {};
var viewObjects = {};
if (isUpdate) {
$(view).find("element").each(function(viewObj) {
var concept = viewObj.concept;
if (concept) {
existingElementIds[concept.id] = true;
viewObjects[concept.id] = viewObj;
}
});
$(view).find("relationship").each(function(viewRel) {
var concept = viewRel.concept;
if (concept) {
existingRelationshipIds[concept.id] = true;
}
});
console.log("Existing elements in view: " + Object.keys(existingElementIds).length);
console.log("Existing relationships in view: " + Object.keys(existingRelationshipIds).length);
}
// Collect all elements to add
var elementsMap = {};
// Add filtered elements
for (var i = 0; i < elements.length; i++) {
elementsMap[elements[i].id] = elements[i];
}
// Add elements connected to filtered relationships
for (var i = 0; i < relationships.length; i++) {
var rel = relationships[i];
if (rel.source) {
elementsMap[rel.source.id] = rel.source;
}
if (rel.target) {
elementsMap[rel.target.id] = rel.target;
}
}
// Convert map to array
var allElements = [];
for (var id in elementsMap) {
allElements.push(elementsMap[id]);
}
console.log("Total elements to consider: " + allElements.length);
console.log("Relationships to consider: " + relationships.length);
// Add missing elements to the view
var addedElementCount = 0;
var x = 50;
var y = 50;
var maxPerRow = 5;
var count = 0;
// Calculate starting position for new elements (if updating)
if (isUpdate && Object.keys(viewObjects).length > 0) {
// Find the bottom-right position of existing elements
var maxX = 0;
var maxY = 0;
for (var id in viewObjects) {
var bounds = viewObjects[id].bounds;
if (bounds) {
var objRight = bounds.x + bounds.width;
var objBottom = bounds.y + bounds.height;
if (objRight > maxX) maxX = objRight;
if (objBottom > maxY) maxY = objBottom;
}
}
x = 50;
y = maxY + 100; // Start below existing elements
}
for (var i = 0; i < allElements.length; i++) {
var element = allElements[i];
// Skip if element already exists in view
if (existingElementIds[element.id]) {
continue;
}
var viewObject = view.add(element, x, y, 120, 55);
viewObjects[element.id] = viewObject;
addedElementCount++;
count++;
if (count % maxPerRow === 0) {
x = 50;
y += 100;
} else {
x += 150;
}
}
// Add missing relationships to the view
var addedRelCount = 0;
for (var i = 0; i < relationships.length; i++) {
var rel = relationships[i];
// Skip if relationship already exists in view
if (existingRelationshipIds[rel.id]) {
continue;
}
// Only add relationship if both source and target are in the view
if (rel.source && rel.target && viewObjects[rel.source.id] && viewObjects[rel.target.id]) {
try {
view.add(rel, viewObjects[rel.source.id], viewObjects[rel.target.id]);
addedRelCount++;
} catch (e) {
console.log("Warning: Could not add relationship: " + e);
}
}
}
if (isUpdate) {
console.log("View updated: " + view.name);
console.log("New elements added: " + addedElementCount);
console.log("New relationships added: " + addedRelCount);
if (addedElementCount === 0 && addedRelCount === 0) {
var mb = new MessageBox(Display.getCurrent().getActiveShell(), SWT.ICON_INFORMATION | SWT.OK);
mb.setText("View Up to Date");
mb.setMessage("All matching items are already in the view.\nNo changes were made.");
mb.open();
} else {
var mb = new MessageBox(Display.getCurrent().getActiveShell(), SWT.ICON_INFORMATION | SWT.OK);
mb.setText("View Updated");
mb.setMessage("Added " + addedElementCount + " element(s) and " + addedRelCount + " relationship(s) to the view.");
mb.open();
}
} else {
console.log("View created: " + view.name);
console.log("Elements added: " + addedElementCount);
console.log("Relationships added: " + addedRelCount);
}
// Open the view
view.openInUI();
}
/**
* Creates the graphical user interface
*/
function createDialog() {
var display = Display.getCurrent();
var shell = new Shell(display, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
shell.setText("Filter by Properties");
shell.setSize(850, 700);
var layout = new GridLayout(1, false);
layout.marginWidth = 10;
layout.marginHeight = 10;
shell.setLayout(layout);
// Target type selection (Elements or Relationships)
var typeGroup = new Group(shell, SWT.NONE);
typeGroup.setText("Target Type");
var typeLayout = new GridLayout(3, false);
typeGroup.setLayout(typeLayout);
var gdTypeGroup = new GridData(SWT.FILL, SWT.CENTER, true, false);
typeGroup.setLayoutData(gdTypeGroup);
var radioElements = new Button(typeGroup, SWT.RADIO);
radioElements.setText("Elements");
radioElements.setSelection(true);
var radioRelationships = new Button(typeGroup, SWT.RADIO);
radioRelationships.setText("Relationships");
var radioBoth = new Button(typeGroup, SWT.RADIO);
radioBoth.setText("Both");
// View selection (Create new or Update existing)
var viewGroup = new Group(shell, SWT.NONE);
viewGroup.setText("View Selection");
var viewLayout = new GridLayout(2, false);
viewGroup.setLayout(viewLayout);
var gdViewGroup = new GridData(SWT.FILL, SWT.CENTER, true, false);
viewGroup.setLayoutData(gdViewGroup);
var radioCreateNew = new Button(viewGroup, SWT.RADIO);
radioCreateNew.setText("Create New View");
radioCreateNew.setSelection(true);
var gdRadioCreate = new GridData(SWT.LEFT, SWT.CENTER, false, false);
radioCreateNew.setLayoutData(gdRadioCreate);
var radioUpdate = new Button(viewGroup, SWT.RADIO);
radioUpdate.setText("Update Existing View:");
var gdRadioUpdate = new GridData(SWT.LEFT, SWT.CENTER, false, false);
radioUpdate.setLayoutData(gdRadioUpdate);
var labelDummy = new Label(viewGroup, SWT.NONE);
labelDummy.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
var comboViews = new Combo(viewGroup, SWT.READ_ONLY | SWT.BORDER);
var gdComboViews = new GridData(SWT.FILL, SWT.CENTER, true, false);
comboViews.setLayoutData(gdComboViews);
comboViews.setEnabled(false);
// Populate views combo
var allViews = getAllViews();
var viewNames = [];
for (var i = 0; i < allViews.length; i++) {
viewNames.push(allViews[i].name);
}
viewNames.sort();
var javaViewNames = java.lang.reflect.Array.newInstance(java.lang.String, viewNames.length);
for (var i = 0; i < viewNames.length; i++) {
javaViewNames[i] = viewNames[i];
}
comboViews.setItems(javaViewNames);
// Store current target type
var currentTargetType = "element";
var propsMap = getAllPropertiesWithValues(currentTargetType);
var propertyNames = Object.keys(propsMap).sort();
// Instructions
var labelInstructions = new Label(shell, SWT.WRAP);
labelInstructions.setText("Select a property on the left, then select values to include in the center (Ctrl+click for multiple selection).\nClick 'Add Filter' to add this criterion.\nLogic: Multiple values for the same property = OR, Different properties = AND.");
var gdInstructions = new GridData(SWT.FILL, SWT.CENTER, true, false);
labelInstructions.setLayoutData(gdInstructions);
// Main area with three columns
var mainComposite = new Composite(shell, SWT.NONE);
var mainLayout = new GridLayout(3, false);
mainComposite.setLayout(mainLayout);
var gdMain = new GridData(SWT.FILL, SWT.FILL, true, true);
mainComposite.setLayoutData(gdMain);
// Column 1: Properties list
var labelProps = new Label(mainComposite, SWT.NONE);
labelProps.setText("Available Properties:");
var gdLabelProps = new GridData(SWT.FILL, SWT.CENTER, false, false);
labelProps.setLayoutData(gdLabelProps);
// Column 2: Values list
var labelValues = new Label(mainComposite, SWT.NONE);
labelValues.setText("Values (Ctrl+click for multiple = OR):");
var gdLabelValues = new GridData(SWT.FILL, SWT.CENTER, true, false);
labelValues.setLayoutData(gdLabelValues);
// Column 3: Selected filters
var labelSelected = new Label(mainComposite, SWT.NONE);
labelSelected.setText("Active Filters:");
var gdLabelSelected = new GridData(SWT.FILL, SWT.CENTER, false, false);
labelSelected.setLayoutData(gdLabelSelected);
// Properties list
var listProps = new ListWidget(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
var gdListProps = new GridData(SWT.FILL, SWT.FILL, false, true);
gdListProps.widthHint = 250;
listProps.setLayoutData(gdListProps);
// Values list (with multiple selection)
var listValues = new ListWidget(mainComposite, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
var gdListValues = new GridData(SWT.FILL, SWT.FILL, true, true);
gdListValues.widthHint = 300;
listValues.setLayoutData(gdListValues);
listValues.setEnabled(false);
// Selected filters list
var listSelected = new ListWidget(mainComposite, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
var gdListSelected = new GridData(SWT.FILL, SWT.FILL, false, true);
gdListSelected.widthHint = 200;
listSelected.setLayoutData(gdListSelected);
// Store selected filters
var selectedFilters = [];
// Function to reload properties based on target type
var reloadProperties = function() {
console.log("Reloading properties for type: " + currentTargetType);
propsMap = getAllPropertiesWithValues(currentTargetType);
propertyNames = Object.keys(propsMap).sort();
if (propertyNames.length === 0) {
listProps.removeAll();
listValues.removeAll();
listValues.setEnabled(false);
console.log("No properties found for " + currentTargetType);
} else {
// Convert propertyNames to Java String[] array
var javaPropertyNames = java.lang.reflect.Array.newInstance(java.lang.String, propertyNames.length);
for (var i = 0; i < propertyNames.length; i++) {
javaPropertyNames[i] = propertyNames[i];
}
listProps.setItems(javaPropertyNames);
listValues.removeAll();
listValues.setEnabled(false);
console.log("Loaded " + propertyNames.length + " properties");
}
};
// Initial load
reloadProperties();
// Buttons below the lists
var btnAddDummy1 = new Label(mainComposite, SWT.NONE);
btnAddDummy1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
var btnAdd = new Button(mainComposite, SWT.PUSH);
btnAdd.setText("Add Filter →");
var gdBtnAdd = new GridData(SWT.FILL, SWT.CENTER, true, false);
btnAdd.setLayoutData(gdBtnAdd);
btnAdd.setEnabled(false);
var btnRemove = new Button(mainComposite, SWT.PUSH);
btnRemove.setText("← Remove");
var gdBtnRemove = new GridData(SWT.FILL, SWT.CENTER, false, false);
btnRemove.setLayoutData(gdBtnRemove);
btnRemove.setEnabled(false);
// Listener for target type change
var SelectionAdapter = Java.type("org.eclipse.swt.events.SelectionAdapter");
radioElements.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (radioElements.getSelection()) {
currentTargetType = "element";
reloadProperties();
}
}
});
radioRelationships.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (radioRelationships.getSelection()) {
currentTargetType = "relationship";
reloadProperties();
}
}
});
radioBoth.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (radioBoth.getSelection()) {
currentTargetType = "both";
// For "both", we'll use element properties as the base
// but will filter both elements and relationships
currentTargetType = "element";
reloadProperties();
currentTargetType = "both";
}
}
});
// Listener for view mode change
radioCreateNew.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (radioCreateNew.getSelection()) {
comboViews.setEnabled(false);
}
}
});
radioUpdate.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (radioUpdate.getSelection()) {
comboViews.setEnabled(true);
}
}
});
// Listener for property selection
listProps.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var selectedIndex = listProps.getSelectionIndex();
if (selectedIndex >= 0) {
var propName = listProps.getItem(selectedIndex);
console.log("Property selected: [" + propName + "]");
// Get values for this property
var valuesObj = propsMap[propName];
if (valuesObj) {
var values = Object.keys(valuesObj).sort();
console.log("Number of values: " + values.length);
// Convert to Java String[] array
var javaValues = java.lang.reflect.Array.newInstance(java.lang.String, values.length);
for (var i = 0; i < values.length; i++) {
javaValues[i] = String(values[i]);
}
listValues.removeAll();
listValues.setItems(javaValues);
listValues.setEnabled(true);
btnAdd.setEnabled(false);
console.log("List updated with " + values.length + " value(s)");
}
}
}
});
// Listener for value selection
listValues.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
btnAdd.setEnabled(listValues.getSelectionCount() > 0);
}
});
// Function to update the filters list
var updateSelectedList = function() {
var items = [];
for (var i = 0; i < selectedFilters.length; i++) {
items.push(selectedFilters[i].propName + " = " + selectedFilters[i].propValue);
}
// Convert to Java String[] array
var javaItems = java.lang.reflect.Array.newInstance(java.lang.String, items.length);
for (var i = 0; i < items.length; i++) {
javaItems[i] = items[i];
}
listSelected.setItems(javaItems);
btnRemove.setEnabled(selectedFilters.length > 0);
};
// Listener for Add button
btnAdd.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var propIndex = listProps.getSelectionIndex();
if (propIndex >= 0) {
var propName = listProps.getItem(propIndex);
var selectedIndices = listValues.getSelectionIndices();
console.log("Adding " + selectedIndices.length + " value(s) for [" + propName + "]");
for (var i = 0; i < selectedIndices.length; i++) {
var value = listValues.getItem(selectedIndices[i]);
selectedFilters.push({
propName: propName,
propValue: value
});
console.log(" Added: [" + propName + "] = [" + value + "]");
}
updateSelectedList();
listValues.deselectAll();
btnAdd.setEnabled(false);
}
}
});
// Listener for Remove button
btnRemove.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
var selectedIndices = listSelected.getSelectionIndices();
if (selectedIndices.length > 0) {
// Remove in reverse order to avoid index shifting
for (var i = selectedIndices.length - 1; i >= 0; i--) {
selectedFilters.splice(selectedIndices[i], 1);
}
updateSelectedList();
}
}
});
// Listener for selected filters list
listSelected.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
btnRemove.setEnabled(listSelected.getSelectionCount() > 0);
}
});
// Control buttons at the bottom
var buttonComposite = new Composite(shell, SWT.NONE);
var gdButtons = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
buttonComposite.setLayoutData(gdButtons);
var buttonLayout = new GridLayout(2, false);
buttonLayout.marginWidth = 0;
buttonComposite.setLayout(buttonLayout);
var btnOK = new Button(buttonComposite, SWT.PUSH);
btnOK.setText("Execute");
var gdBtnOK = new GridData(SWT.FILL, SWT.CENTER, false, false);
gdBtnOK.widthHint = 120;
btnOK.setLayoutData(gdBtnOK);
var btnCancel = new Button(buttonComposite, SWT.PUSH);
btnCancel.setText("Cancel");
var gdBtnCancel = new GridData(SWT.FILL, SWT.CENTER, false, false);
gdBtnCancel.widthHint = 120;
btnCancel.setLayoutData(gdBtnCancel);
// Listener for Execute button
btnOK.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
if (selectedFilters.length === 0) {
var mb = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
mb.setText("Empty Selection");
mb.setMessage("Please add at least one filter.");
mb.open();
return;
}
// Check if updating an existing view
var existingView = null;
if (radioUpdate.getSelection()) {
var selectedViewIndex = comboViews.getSelectionIndex();
if (selectedViewIndex < 0) {
var mb = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
mb.setText("No View Selected");
mb.setMessage("Please select a view to update.");
mb.open();
return;
}
var selectedViewName = comboViews.getItem(selectedViewIndex);
// Find the view by name
for (var i = 0; i < allViews.length; i++) {
if (allViews[i].name === selectedViewName) {
existingView = allViews[i];
break;
}
}
}
shell.close();
console.log("=== " + (existingView ? "UPDATING" : "CREATING") + " VIEW ===");
console.log("Target type: " + currentTargetType);
console.log("Applied filters:");
var filtersByProp = organizeFiltersByProperty(selectedFilters);
for (var propName in filtersByProp) {
console.log(" " + propName + ": " + filtersByProp[propName].join(" OR "));
}
// Find elements and/or relationships based on target type
var elements = [];
var relationships = [];
if (currentTargetType === "element" || currentTargetType === "both") {
elements = findElementsByProperties(selectedFilters);
console.log("Elements found: " + elements.length);
}
if (currentTargetType === "relationship" || currentTargetType === "both") {
relationships = findRelationshipsByProperties(selectedFilters);
console.log("Relationships found: " + relationships.length);
}
// Create or update the view
createOrUpdateView(elements, relationships, selectedFilters, currentTargetType, existingView);
}
});
// Listener for Cancel button
btnCancel.addSelectionListener(new SelectionAdapter() {
widgetSelected: function(e) {
shell.close();
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
// Run the script
createDialog();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment