Commit 70df55f9 authored by Martti Käärik's avatar Martti Käärik
Browse files

Model generator: preserve null checks on operation results and use one OCL environment per run

parent e19ddc9e
Loading
Loading
Loading
Loading
+67 −0
Changes for plugins/org.etsi.mts.tdl.model.gen/src/org/etsi/mts/tdl/gen/NullableOperationCallCGModelFactory.java: 67 added lines, 0 removed lines.
Original line number Diff line number Diff line
package org.etsi.mts.tdl.gen;

import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.ocl.examples.codegen.cgmodel.CGEcoreOperationCallExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGModelFactory;
import org.eclipse.ocl.examples.codegen.cgmodel.CGModelPackage;
import org.eclipse.ocl.examples.codegen.cgmodel.impl.CGEcoreOperationCallExpImpl;
import org.eclipse.ocl.examples.codegen.cgmodel.impl.CGModelFactoryImpl;

/**
 * Works around a defect of the OCL code generator (Eclipse OCL 6.22 / codegen 2.22):
 * {@code CGEcoreOperationCallExpImpl.isNonNull()} is hard-coded to {@code true}, so the
 * result of every call to a metamodel operation is assumed non-null and
 * {@code self.op().oclIsUndefined()} is folded to {@code false} at generation time.
 * <p>
 * The generator obtains CG model objects from {@code CGModelFactory.eINSTANCE}, a constant
 * initialised from the global package registry on first use; registering this factory
 * for the CG model's namespace before anything touches the CG model makes the generator
 * create {@link CGEcoreOperationCallExp}s whose nullity is the computed one. Must run
 * before the first generation of the workflow; {@link #install()} reports whether it was
 * in time.
 */
public final class NullableOperationCallCGModelFactory extends CGModelFactoryImpl {

	private static final Logger LOG = Logger.getLogger(NullableOperationCallCGModelFactory.class);
	private static boolean installed;

	@Override
	public CGEcoreOperationCallExp createCGEcoreOperationCallExp() {
		return new CGEcoreOperationCallExpImpl() {
			@Override
			public boolean isNonNull() {
				return isRequired();
			}
		};
	}

	/** Registers the factory once; logs an error if the CG model was already initialised (fix inactive). */
	public static synchronized void install() {
		if (installed) {
			return;
		}
		installed = true;
		final NullableOperationCallCGModelFactory factory = new NullableOperationCallCGModelFactory();
		if (!EPackage.Registry.INSTANCE.containsKey(CGModelPackage.eNS_URI)) { // eNS_URI is a compile-time constant: no class init
			EPackage.Registry.INSTANCE.put(CGModelPackage.eNS_URI, new EPackage.Descriptor() {
				@Override
				public EPackage getEPackage() {
					return CGModelPackage.eINSTANCE;
				}

				@Override
				public EFactory getEFactory() {
					return factory;
				}
			});
		}
		if (CGModelFactory.eINSTANCE instanceof NullableOperationCallCGModelFactory) {
			LOG.info("OCL code generator: operation-call nullity fix active");
		} else {
			LOG.error("OCL code generator: operation-call nullity fix NOT active (CG model initialised before install); "
					+ "oclIsUndefined() on operation results will be folded away — do not ship this generation");
		}
	}
}
+11 −1
Changes for plugins/org.etsi.mts.tdl.model.gen/src/org/etsi/mts/tdl/gen/OCLinEcoreGenerator.java: 11 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -23,8 +23,12 @@ public class OCLinEcoreGenerator extends EcoreGeneratorCustom {
	private String projectRoot = "";
	private static Logger log = Logger.getLogger(OCLinEcoreGenerator.class);

	private static OCL ocl;

	@Override
	public void invoke(IWorkflowContext ctx) {
		// Before anything touches the OCL code generator's CG model (see the class comment there)
		NullableOperationCallCGModelFactory.install();
		log.info("Running on: "+projectRoot);
		//TODO: handle if unresolved?
		String codeGenJar = locateContainingJar(Generator.class);
@@ -44,8 +48,14 @@ public class OCLinEcoreGenerator extends EcoreGeneratorCustom {
        		URI.createFileURI(projectRoot.getAbsolutePath() + File.separator)
    	);

        // Use one OCL environment for all genmodels of a workflow run. A second OCL instance on the
        // same thread puts ThreadLocalExecutor into "concurrent EnvironmentFactory" mode, in which
        // the OCL genmodel adapter builds its own environment over our resource set and the first
        // class of every later package gets a "Missing specification body".
        if (ocl == null) {
            OCLinEcoreStandaloneSetup.doSetup();
        OCL ocl = OCL.newInstance();
            ocl = OCL.newInstance();
        }
        ResourceSet resourceSet = ocl.getResourceSet();
        setResourceSet(resourceSet);