SteKoe / ocl.js

The Object Constraint Language (OCL) is a language for describing rules that apply to MOF conform modelling languages like UML. The OCL is a text based language that provides constraint and object query expressions that cannot be expressed by a meta modelling language.
http://ocl.stekoe.de
Other
55 stars 10 forks source link

How to build up the models? #13

Closed pmeijer closed 6 years ago

pmeijer commented 6 years ago

It's not clear to me how to use the API of this project. Specifically I'm having problems of building up the "instance" models to be checked. I'm very interested in integrating OCL into a metamodeling framework, but after playing around with the ocl.js I can't get it to work. I've included a snippet, any pointers are welcome. (I'm using the structure below since I'm intending to generate the models to be evaluated on the fly based on the models in our framework.)

global.window = undefined; // Needed to avoid exception at import..
const OCLEngine = require("@stekoe/ocl.js");

function OCLObject(type, attributes, pointers, sets) {
    this.type = type;
    this.attributes = attributes;
    this.pointers = pointers;
    this.sets = sets;
}

// Super-classes
const Person = new OCLObject('Person', {name: 'Person', isMale: true}, {dad: null, mom: null}, {});
const Family = new OCLObject('Family', {name: 'Family', address: ''}, {}, {familyMembers: []});

// "Instances"
const aFamily = Object.assign(Object.create(Family), {
    attributes: {name: 'Smiths', address: 'Main Street'},
});

const dad = Object.assign(Object.create(Person), {
    attributes: {name: 'Joe', isMale: true},
});

const mom = Object.assign(Object.create(Person), {
    attributes: {name: 'Jen', isMale: false},
});

const son = Object.assign(Object.create(Person), {
    attributes: {name: 'JoeJr', isMale: true},
    pointers: {dad: dad, mom: mom}
});

const daughter = Object.assign(Object.create(Person), {
    attributes: {name: 'JenJr', isMale: false},
    pointers: {dad: dad, mom: mom}
});

const derp = Object.assign(Object.create(Person), {
    attributes: {name: 'derp', isMale: false},
    pointers: {mom: dad, dad: mom}
});

Object.assign(aFamily, {sets: {familyMembers: [dad, mom, son, daughter, derp]}});

// Check the instance model
const oclEngine = new OCLEngine();

const dadIsMale = `
    -- Check that dad is indeed a male
    context Person
        inv: self.pointers.dad->notEmpty() implies self.pointers.dad.isMale = true
`;

oclEngine.addOclExpression(dadIsMale);

var res = oclEngine.evaluate(aFamily);
console.log(res); // t { namesOfFailedInvs: [], evaluatedContexts: [], result: true }
res = oclEngine.evaluate(son);
console.log(res); // t { namesOfFailedInvs: [], evaluatedContexts: [], result: true }
res = oclEngine.evaluate(derp);
console.log(res); // t { namesOfFailedInvs: [], evaluatedContexts: [], result: true }
JDziurlaj commented 6 years ago

It looks like ocl.js is not matching the context (class named Person) against any of the objects you evaluated. You should take a look at setTypeDeterminer and make sure that your objects type can be determined.

  oclEngine.setTypeDeterminer((obj: any) => {
                // name of the class is type name
                return obj.constructor.name;
  });
JDziurlaj commented 6 years ago

Additionally your OCL invariant is structured incorrectly. Given your objects, it should look more like this:

const dadIsMale = `
    -- Check that dad is indeed a male
    context Person
        inv: (not self.pointers.dad.oclIsUndefined()) implies self.pointers.dad.attributes.isMale = true
`;
SteKoe commented 6 years ago

Hey y'all! Thanks for the question and @JDziurlaj thanks for helping out! I have read your email but have not found the time to answer, yet, but your help is very much appreciated!

@pmeijer As @JDziurlaj already mentioned, you have to define a custom typeDeterminer function that will take any object as parameter which is passed into the .evaluate function. I assume that the field type is the discriminator in your example, hence a corresponding function for your use case should look like and work as follows:

  oclEngine.setTypeDeterminer((obj: any) => {
                return obj.type; // ob.type will contain 'Person' or 'Family' 
  });

Hope we were able to help you a little bit and I promise to optimize the API documentation as soon as possible!

SteKoe commented 6 years ago

I have extended the documentation regarding this feature: http://ocl.stekoe.de/usage.html#provide-own-typedeterminer

pmeijer commented 6 years ago

Thank you both for your replies. The documentation is thorough in regards to supported OCL expressions - which is great since I'm new to OCL. However it would be really good to generate jsdoc documentation for the OclEngine amd OclResult.

pmeijer commented 6 years ago

Changing the invariant still leads to issues with dad though. Technically dad.pointers.dad is null and not undefined.

const dadIsMale = `
    -- Check that dad is indeed a male
    context Person
        inv: (not self.pointers.dad.oclIsUndefined()) implies self.pointers.dad.attributes.isMale = true
`;

oclEngine.addOclExpression(dadIsMale);

let res = oclEngine.evaluate(aFamily);
console.log(res.getResult()); // true, since the Family Context itself is fine

res = oclEngine.evaluate(son);
console.log(res.getResult()); // true

res = oclEngine.evaluate(dad);
console.log(res.getResult()); // true, dad doesn't define a dad (but returns false)

res = oclEngine.evaluate(derp);
console.log(res.getResult()); // false, derps dad is Jen
JDziurlaj commented 6 years ago

I'm not sure what self.pointers.dad.bogus() is, but I'm assuming that the expression is being evaluated as false or undefined, and the not is causing it to become true and evaluate the implication.

pmeijer commented 6 years ago

Changing to const Person = new OCLObject('Person', {name: 'Person', isMale: true}, {dad: undefined, mom: undefined}, {}); works.

You're right I sloppily forgot the not was added.

SteKoe commented 6 years ago

@pmeijer I have added two issues which should remind me to add more API documentation. Shame on me for not having done that yet.

narayanan79 commented 3 years ago

@pmeijer I have added two issues which should remind me to add more API documentation. Shame on me for not having done that yet.

Hi @SteKoe ,

I am new to OCL.

evaluatedContexts Array is always empty in my example. image

What is wrong in the code?

https://codesandbox.io/s/rule-engine-ocl-js-z38c2?file=/src/App.js