diff --git a/__pycache__/compare_archimate.cpython-38.pyc b/__pycache__/compare_archimate.cpython-38.pyc new file mode 100644 index 00000000..c10b01db Binary files /dev/null and b/__pycache__/compare_archimate.cpython-38.pyc differ diff --git a/compare_archimate.py b/compare_archimate.py new file mode 100644 index 00000000..116cf265 --- /dev/null +++ b/compare_archimate.py @@ -0,0 +1,436 @@ +#!/usr/bin/env python3 +""" +ArchiMate XML Semantic Comparison Tool + +Compares two ArchiMate 3.x XML files for structural equivalence: +- Model identifier and name +- Element counts, identifiers, and types +- Relationship counts, identifiers, types, and source/target +- View counts and identifiers +- Organization item counts +- PropertyDefinition counts, identifiers, and names +- Type distribution matching + +Usage: + python3 compare_archimate.py [--json] + +Exit codes: + 0 = all checks passed + 1 = one or more checks failed + 2 = error (file not found, parse error, etc.) +""" + +import sys +import json as json_module +import xml.etree.ElementTree as ET +from collections import Counter + +NS = {"am": "http://www.opengroup.org/xsd/archimate/3.0/"} +XSI = "http://www.w3.org/2001/XMLSchema-instance" + + +def parse_archimate(filepath): + """Parse an ArchiMate XML file and extract structural info.""" + tree = ET.parse(filepath) + root = tree.getroot() + + info = { + "model_id": root.get("identifier", ""), + "model_name": "", + "elements": {}, + "relationships": {}, + "views": {}, + "organizations": [], + "property_definitions": {}, + } + + # Model name + name_el = root.find("am:name", NS) + if name_el is not None: + info["model_name"] = (name_el.text or "").strip() + + # Elements + for el in root.findall(".//am:elements/am:element", NS): + eid = el.get("identifier", "") + etype = el.get(f"{{{XSI}}}type", "") + if eid: + info["elements"][eid] = etype + + # Relationships + for rel in root.findall(".//am:relationships/am:relationship", NS): + rid = rel.get("identifier", "") + rtype = rel.get(f"{{{XSI}}}type", "") + source = rel.get("source", "") + target = rel.get("target", "") + if rid: + info["relationships"][rid] = { + "type": rtype, + "source": source, + "target": target, + } + + # Views (diagrams) + for view in root.findall(".//am:views/am:diagrams/am:view", NS): + vid = view.get("identifier", "") + vname_el = view.find("am:name", NS) + vname = (vname_el.text or "").strip() if vname_el is not None else "" + if vid: + info["views"][vid] = vname + + # Organizations — count all elements recursively. + # ArchiMate organizations use with optional identifierRef (element references) + # or