Commit 66b09e33 authored by Philip Makedonski's avatar Philip Makedonski
Browse files

+ added model comparison

parent 83fffcff
Loading
Loading
Loading
Loading
+26 −0
Changes for plugins/org.etsi.mts.tdl.constraints.helper/src/org/etsi/mts/tdl/constraints/helper/ClassIR.java: 26 added lines, 0 removed lines.
Original line number Diff line number Diff line
package org.etsi.mts.tdl.constraints.helper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

class ClassIR {
	String name;
	String superType = "";
	List<PropertyIR> properties = new ArrayList<>();

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof ClassIR)) return false;
		ClassIR other = (ClassIR) o;
		return Objects.equals(name, other.name)
			&& Objects.equals(superType, other.superType)
			&& Objects.equals(properties, other.properties);
	}

	@Override
	public int hashCode() {
		return Objects.hash(name, superType, properties);
	}
}
+311 −0
Changes for plugins/org.etsi.mts.tdl.constraints.helper/src/org/etsi/mts/tdl/constraints/helper/ModelComparison.java: 311 added lines, 0 removed lines.
Original line number Diff line number Diff line
package org.etsi.mts.tdl.constraints.helper;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

public class ModelComparison {

	public static void main(String[] args) {
		if (args == null || args.length == 0) {
			System.out.println("Usage: ModelComparison <path-to-ecore-file> [<other-ecore-file>]");
			return;
		}
		var roots = loadModel(args[0]);
		ModelIR modelA = buildModelIR(roots);
		renderModel(modelA);
		// If a second model path is provided, load and compare
		if (args.length > 1) {
			var rootsB = loadModel(args[1]);
			ModelIR modelB = buildModelIR(rootsB);
			renderModel(modelB);
			compareModels(modelA, modelB);
		}
	}
	
	private static void processRoots(List<EObject> roots) {
		// kept for compatibility; now using buildModelIR in main
		for (var root : roots) {
			processRoot(root);
		}
	}

	private static void processRoot(EObject root) {
		// kept for compatibility; when needed this can be adapted to build pieces
		for (var c : root.eContents()) {
			if (c instanceof EClass) {
				var cls = ((EClass) c);
				renderEClass(cls);
			}
		}
	}

	// --- New: build intermediate representation (IR) ---
	static ModelIR buildModelIR(List<EObject> roots) {
		ModelIR model = new ModelIR();
		if (roots == null) {
			return model;
		}
		for (EObject root : roots) {
			if (root instanceof EPackage) {
				EPackage pkg = (EPackage) root;
				model.packageName = pkg.getName();
				model.packageNsURI = pkg.getNsURI();
				for (EObject c : pkg.eContents()) {
					if (c instanceof EClass) {
						EClass cls = (EClass) c;
						ClassIR classIR = new ClassIR();
						classIR.name = cls.getName();
						if (!cls.getESuperTypes().isEmpty()) {
							classIR.superType = cls.getESuperTypes().get(0).getName();
						}
						for (EStructuralFeature f : cls.getEStructuralFeatures()) {
							PropertyIR p = new PropertyIR();
							p.name = f.getName();
							p.lower = f.getLowerBound();
							p.upper = f.getUpperBound();
							p.typeName = f.getEType() != null ? f.getEType().getName() : "<unspecified>";
							if (p.typeName == null) {
								p.typeName = "<unspecified>";
							}
							if (p.typeName.equals("EString")) {
								p.typeName = "String";
							}
							if (p.typeName.equals("EBoolean") || p.typeName.equals("EBooleanObject") ) {
								p.typeName = "Boolean";
							}
							if (p.typeName.equals("EBigInteger")) {
								p.typeName = "Integer";
							}
							p.modifiers = getModifiersList(f);
							classIR.properties.add(p);
						}
						model.classes.add(classIR);
					}
				}
			}
		}
		return model;
	}

	private static List<String> getModifiersList(EStructuralFeature f) {
		List<String> modifiers = new ArrayList<>();
//		if (f.isOrdered()) {
//			modifiers.add("ordered");
//		}
//		if (f.isUnique()) {
//			modifiers.add("unique");
//		}
//		if (!f.isChangeable()) {
//			// note: original code added "readOnly" when isChangeable() was true, but that appears inverted
//			// preserve original behaviour if needed; here we use readOnly when not changeable
//			modifiers.add("readOnly");
//		}
		return modifiers;
	}

	// --- New: render from IR ---
	private static void renderModel(ModelIR model) {
		System.out.println("Model package='" + model.packageName + "' nsURI='" + model.packageNsURI + "'");
		for (ClassIR cls : model.classes) {
			renderClassIR(cls);
		}
	}

	private static void renderClassIR(ClassIR cls) {
		String superType = cls.superType == null || cls.superType.isEmpty() ? "" : " extends " + cls.superType;
		System.out.println("class " + cls.name + superType);
		for (PropertyIR p : cls.properties) {
			String ub = Integer.toString(p.upper);
			if (p.upper == -1) {
				ub = "*";
			}
			String bounds = " [" + p.lower + ".." + ub + "]";
			if (p.upper == p.lower) {
				bounds = " [" + p.upper + "]";
			}
			String modifier = "";
			if (!p.modifiers.isEmpty()) {
				modifier = " { " + String.join(" ", p.modifiers) + " }";
			}
			System.out.println("    property " + p.name + ": " + p.typeName + bounds + modifier + ";");
		}
	}

	// --- New: compare two models (simple diff) ---
	static void compareModels(ModelIR a, ModelIR b) {
		System.out.println("Comparing models: '" + a.packageName + "' vs '" + b.packageName + "'");
		if (Objects.equals(a, b)) {
			System.out.println("Models are equal");
			return;
		}
		// find classes only in A
		Set<String> namesA = new HashSet<>();
		for (ClassIR c : a.classes) namesA.add(c.name);
		Set<String> namesB = new HashSet<>();
		for (ClassIR c : b.classes) namesB.add(c.name);
		for (String n : namesA) {
			if (!namesB.contains(n)) {
				System.out.println("Only in A: class " + n);
			}
		}
		for (String n : namesB) {
			if (!namesA.contains(n)) {
				System.out.println("Only in B: class " + n);
			}
		}
		// for shared classes, print property differences
		for (ClassIR ca : a.classes) {
			for (ClassIR cb : b.classes) {
				if (Objects.equals(ca.name, cb.name)) {
					if (!Objects.equals(ca, cb)) {
						System.out.println("Differences in class: " + ca.name);
						if (!ca.superType.equals(cb.superType)) {
							System.out.println("  Mismatching super types: "+ca.superType +" vs "+cb.superType);
						}
						// simple property diff by name
						Set<String> pa = new HashSet<>();
						for (PropertyIR p : ca.properties) pa.add(p.name);
						Set<String> pb = new HashSet<>();
						for (PropertyIR p : cb.properties) pb.add(p.name);
						for (String pn : pa) if (!pb.contains(pn)) System.out.println("  Only in A: property " + pn);
						for (String pn : pb) if (!pa.contains(pn)) System.out.println("  Only in B: property " + pn);
						System.out.println("-----------------------------");
						renderClassIR(ca);
						renderClassIR(cb);
						System.out.println("-----------------------------");
					}
				}
			}
		}
	}

	// --- existing rendering code preserved for compatibility ---
	private static void renderEClass(EClass cls) {
		String superType = "";
		if (!cls.getESuperTypes().isEmpty()) {
			superType = " extends "+cls.getESuperTypes().get(0).getName();
		}
		System.out.println("class "+cls.getName() + superType);
		for (var f : cls.getEStructuralFeatures()) {
			String modifier = getModifiers(f);
			int upperBound = f.getUpperBound();
			String ub = Integer.toString(upperBound);
			if (upperBound == -1) {
				ub = "*";
			}
			String bounds = " ["+f.getLowerBound()+".."+ub+"]";
			if (f.getUpperBound()==f.getLowerBound()) {
				bounds = " [" +f.getUpperBound()+ "]";
			}
			System.out.println("    property "+f.getName() +": "+f.getEType().getName()+bounds+modifier+";");
		}
	}

	private static String getModifiers(EStructuralFeature f) {
		String modifier = "";
//		List<String> modifiers = new ArrayList<>();
//		if (f.isOrdered()) {
//			modifiers.add("ordered");
//		}
//		if (f.isUnique()) {
//			modifiers.add("unique");
//		}
//		if (f.isChangeable()) {
//			modifiers.add("readOnly");
//		}
//		if (!modifiers.isEmpty()) {
//			modifier = " { "+String.join(" ", modifiers)+" }";
//		}
		return modifier;
	}



	public static List<EObject> loadModel(String path) {
		if (path == null || path.trim().isEmpty()) {
			System.err.println("loadModel: provided path is null or empty");
			return null;
		}

		try {
			// Ensure Ecore package is registered
			EcorePackage.eINSTANCE.eClass();

			// Create a resource set and register a factory for .ecore (XMI)
			ResourceSet resourceSet = new ResourceSetImpl();
			resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap()
					.put("ecore", new XMIResourceFactoryImpl());

			// Resolve the incoming path to an EMF URI. Accepts absolute/relative file paths and explicit URIs.
			URI uri = getURI(path);

			//TODO: figure out better way to do this
//			String ecorePath = "../org.etsi.mts.tdl.model/model/tdl.ecore";
//			Resource baseResource = resourceSet.getResource(getURI(ecorePath), true);
//			baseResource.load(null);

			// Load the resource
			Resource resource = resourceSet.getResource(uri, true);
			resource.load(null);
			EcoreUtil.resolveAll(resourceSet);

			List<EObject> roots = resource.getContents();
			System.out.println("Loaded resource: " + uri + " (" + roots.size() + " root elements)");
			for (EObject root : roots) {
				if (root instanceof EPackage) {
					EPackage pkg = (EPackage) root;
					System.out.println("EPackage: name='" + pkg.getName() + "' nsURI='" + pkg.getNsURI() + "'");
				} else {
					System.out.println("Root element: " + root.eClass().getName());
				}
			}
			return roots;

		} catch (IOException e) {
			System.err.println("IOException while loading model from path='" + path + "': " + e.getMessage());
			e.printStackTrace(System.err);
		} catch (RuntimeException e) {
			System.err.println("Error while loading model from path='" + path + "': " + e.getMessage());
			e.printStackTrace(System.err);
		}
		return null;
	
	}

	private static URI getURI(String path) {
		URI uri;
		String trimmed = path.trim();
		if (trimmed.startsWith("platform:") || trimmed.startsWith("file:") || trimmed.startsWith("http:")
				|| trimmed.startsWith("https:") || trimmed.startsWith("jar:")) {
			uri = URI.createURI(trimmed);
		} else {
			File f = new File(trimmed);
			if (f.isAbsolute()) {
				uri = URI.createFileURI(f.getAbsolutePath());
			} else {
				// treat as relative file path
				uri = URI.createFileURI(f.getPath());
			}
		}
		return uri;
	}


}
 No newline at end of file
+26 −0
Changes for plugins/org.etsi.mts.tdl.constraints.helper/src/org/etsi/mts/tdl/constraints/helper/ModelIR.java: 26 added lines, 0 removed lines.
Original line number Diff line number Diff line
package org.etsi.mts.tdl.constraints.helper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

class ModelIR {
	String packageName = "";
	String packageNsURI = "";
	List<ClassIR> classes = new ArrayList<>();

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof ModelIR)) return false;
		ModelIR other = (ModelIR) o;
		return Objects.equals(packageName, other.packageName)
			&& Objects.equals(packageNsURI, other.packageNsURI)
			&& Objects.equals(classes, other.classes);
	}

	@Override
	public int hashCode() {
		return Objects.hash(packageName, packageNsURI, classes);
	}
}
+31 −0
Changes for plugins/org.etsi.mts.tdl.constraints.helper/src/org/etsi/mts/tdl/constraints/helper/PropertyIR.java: 31 added lines, 0 removed lines.
Original line number Diff line number Diff line
package org.etsi.mts.tdl.constraints.helper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

class PropertyIR {
	String name;
	String typeName;
	int lower;
	int upper;
	List<String> modifiers = new ArrayList<>();

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof PropertyIR)) return false;
		PropertyIR other = (PropertyIR) o;
		return Objects.equals(name, other.name)
			&& Objects.equals(typeName, other.typeName)
			&& lower == other.lower
			&& upper == other.upper
			//&& Objects.equals(modifiers, other.modifiers)
			;
	}

	@Override
	public int hashCode() {
		return Objects.hash(name, typeName, lower, upper, modifiers);
	}
}