///* // * generated by Xtext 2.25.0 // */ package ca.uottawa.csmlab.symboleo.generator import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.AbstractGenerator import org.eclipse.xtext.generator.IFileSystemAccess2 import org.eclipse.xtext.generator.IGeneratorContext import ca.uottawa.csmlab.symboleo.symboleo.DomainType import ca.uottawa.csmlab.symboleo.symboleo.RegularType import ca.uottawa.csmlab.symboleo.symboleo.Attribute import java.util.List import ca.uottawa.csmlab.symboleo.symboleo.Model import java.util.ArrayList import ca.uottawa.csmlab.symboleo.symboleo.Enumeration import ca.uottawa.csmlab.symboleo.symboleo.Parameter //import ca.uottawa.csmlab.symboleo.symboleo.AssignVariable import ca.uottawa.csmlab.symboleo.symboleo.VariableRef import ca.uottawa.csmlab.symboleo.symboleo.Ref import ca.uottawa.csmlab.symboleo.symboleo.VariableDotExpression import ca.uottawa.csmlab.symboleo.symboleo.AssignExpression import ca.uottawa.csmlab.symboleo.symboleo.OAssignExpression import ca.uottawa.csmlab.symboleo.symboleo.Expression import ca.uottawa.csmlab.symboleo.symboleo.And import ca.uottawa.csmlab.symboleo.symboleo.Or import ca.uottawa.csmlab.symboleo.symboleo.Equality import ca.uottawa.csmlab.symboleo.symboleo.Comparison import ca.uottawa.csmlab.symboleo.symboleo.Plus import ca.uottawa.csmlab.symboleo.symboleo.Minus import ca.uottawa.csmlab.symboleo.symboleo.Div import ca.uottawa.csmlab.symboleo.symboleo.Multi import ca.uottawa.csmlab.symboleo.symboleo.PrimaryExpressionFunctionCall import ca.uottawa.csmlab.symboleo.symboleo.NegatedPrimaryExpression import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionTrue import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionFalse import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionDouble import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionInt import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionEnum import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionString import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionParameter import ca.uottawa.csmlab.symboleo.symboleo.PrimaryExpressionRecursive import ca.uottawa.csmlab.symboleo.symboleo.TwoArgMathFunction import ca.uottawa.csmlab.symboleo.symboleo.OneArgMathFunction import ca.uottawa.csmlab.symboleo.symboleo.TwoArgStringFunction import ca.uottawa.csmlab.symboleo.symboleo.OneArgStringFunction import ca.uottawa.csmlab.symboleo.symboleo.Obligation import ca.uottawa.csmlab.symboleo.symboleo.Power import ca.uottawa.csmlab.symboleo.symboleo.Variable import ca.uottawa.csmlab.symboleo.symboleo.Proposition import ca.uottawa.csmlab.symboleo.symboleo.POr import ca.uottawa.csmlab.symboleo.symboleo.PAnd import ca.uottawa.csmlab.symboleo.symboleo.PEquality import ca.uottawa.csmlab.symboleo.symboleo.NegatedPAtom import ca.uottawa.csmlab.symboleo.symboleo.PAtomPredicate import ca.uottawa.csmlab.symboleo.symboleo.PAtomEnum import ca.uottawa.csmlab.symboleo.symboleo.PAtomRecursive import ca.uottawa.csmlab.symboleo.symboleo.PComparison import ca.uottawa.csmlab.symboleo.symboleo.PAtomVariable import ca.uottawa.csmlab.symboleo.symboleo.PAtomPredicateTrueLiteral import ca.uottawa.csmlab.symboleo.symboleo.PAtomPredicateFalseLiteral import ca.uottawa.csmlab.symboleo.symboleo.PAtomIntLiteral import ca.uottawa.csmlab.symboleo.symboleo.PAtomStringLiteral import java.util.HashMap import ca.uottawa.csmlab.symboleo.symboleo.PAtomDoubleLiteral import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunction import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionHappens import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionHappensWithin import ca.uottawa.csmlab.symboleo.symboleo.Event import ca.uottawa.csmlab.symboleo.symboleo.VariableEvent import ca.uottawa.csmlab.symboleo.symboleo.PowerEvent import ca.uottawa.csmlab.symboleo.symboleo.ObligationEvent import ca.uottawa.csmlab.symboleo.symboleo.ContractEvent import ca.uottawa.csmlab.symboleo.symboleo.PFObligationSuspended import ca.uottawa.csmlab.symboleo.symboleo.PFObligationResumed import ca.uottawa.csmlab.symboleo.symboleo.PFObligationDischarged import ca.uottawa.csmlab.symboleo.symboleo.PFObligationTerminated import ca.uottawa.csmlab.symboleo.symboleo.PFContractSuspended import ca.uottawa.csmlab.symboleo.symboleo.PFContractResumed import ca.uottawa.csmlab.symboleo.symboleo.PFContractTerminated import ca.uottawa.csmlab.symboleo.symboleo.Interval import ca.uottawa.csmlab.symboleo.symboleo.Point import ca.uottawa.csmlab.symboleo.symboleo.PointExpression import ca.uottawa.csmlab.symboleo.symboleo.PointAtomParameterDotExpression import ca.uottawa.csmlab.symboleo.symboleo.PointFunction import ca.uottawa.csmlab.symboleo.symboleo.PointAtomObligationEvent import ca.uottawa.csmlab.symboleo.symboleo.PointAtomContractEvent import ca.uottawa.csmlab.symboleo.symboleo.PointAtomPowerEvent import ca.uottawa.csmlab.symboleo.symboleo.IntervalExpression import ca.uottawa.csmlab.symboleo.symboleo.IntervalFunction import ca.uottawa.csmlab.symboleo.symboleo.Situation import ca.uottawa.csmlab.symboleo.symboleo.ObligationState import ca.uottawa.csmlab.symboleo.symboleo.PowerState import ca.uottawa.csmlab.symboleo.symboleo.ContractState import ca.uottawa.csmlab.symboleo.symboleo.SituationExpression import ca.uottawa.csmlab.symboleo.symboleo.Timevalue import ca.uottawa.csmlab.symboleo.symboleo.TimevalueInt import ca.uottawa.csmlab.symboleo.symboleo.TimevalueVariable import ca.uottawa.csmlab.symboleo.symboleo.ThreeArgStringFunction import ca.uottawa.csmlab.symboleo.symboleo.ThreeArgDateFunction import ca.uottawa.csmlab.symboleo.Helpers import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionSHappensBefore import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionWHappensBefore import ca.uottawa.csmlab.symboleo.symboleo.OntologyType import ca.uottawa.csmlab.symboleo.symboleo.AtomicExpressionDate import ca.uottawa.csmlab.symboleo.symboleo.PAtomDateLiteral import java.time.format.DateTimeFormatter import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionHappensAfter import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionAssignment import ca.uottawa.csmlab.symboleo.symboleo.PredicateFunctionAssignmentOnly import ca.uottawa.csmlab.symboleo.symboleo.PFObligationTriggered import ca.uottawa.csmlab.symboleo.symboleo.Assignment import ca.uottawa.csmlab.symboleo.symboleo.OAssignment // /** * Generates code from your model files on save. * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation */ class SymboleoGenerator extends AbstractGenerator { val ASSET_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val EVENT_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val ROLE_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val POWER_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val OBLIGATION_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val CONTRACT_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val EVENTS_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val PREDICATES_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val UTILS_CLASS_IMPORT_PATH = "\"symboleo-js-core\"" val assets = new ArrayList val events = new ArrayList val roles = new ArrayList val enumerations = new ArrayList val parameters = new ArrayList val variables = new ArrayList val AssignVar= new ArrayList val conditionalObligations = new ArrayList val conditionalSurvivingObligations = new ArrayList val conditionalPowers = new ArrayList val unconditionalObligations = new ArrayList val unconditionalSurvivingObligations = new ArrayList val unconditionalPowers = new ArrayList val untriggeredObligations = new ArrayList val untriggeredSurvivingObligations = new ArrayList val untriggeredPowers = new ArrayList val triggeredObligations = new ArrayList val triggeredSurvivingObligations = new ArrayList val triggeredPowers = new ArrayList val allObligations = new ArrayList val allSurvivingObligations = new ArrayList val allPowers = new ArrayList val arrays = new ArrayList val eventVariables = new ArrayList val obligationTriggerEvents = new HashMap> val survivingObligationTriggerEvents = new HashMap> val powerTriggerEvents = new HashMap> val obligationAntecedentEvents = new HashMap> val survivingObligationAntecedentEvents = new HashMap> val powerAntecedentEvents = new HashMap> val obligationFullfilmentEvents = new HashMap> val survivingObligationFullfilmentEvents = new HashMap> def void generateHFSource(IFileSystemAccess2 fsa, Model model) { parse(model) compileDomainTypes(fsa, model) compileContract(fsa, model) compileEventsFile(fsa, model) compileTransactionFile(fsa, model) compileSerializerFile(fsa, model) generateNPMFile(fsa, model) } def void parse(Model model) { parameters.addAll(model.parameters) variables.addAll(model.variables) for (domainType : model.domainTypes) { if (domainType instanceof RegularType) { var RegularType base = Helpers.getBaseType(domainType) if (base !== null) { switch base.ontologyType.name { case 'Asset': assets.add(domainType as RegularType) case 'Event': events.add(domainType as RegularType) case 'Role': roles.add(domainType as RegularType) } } } else if (domainType instanceof Enumeration) { enumerations.add(domainType as Enumeration) } } // event variables for (variable : model.variables) { if (events.indexOf(variable.type) != -1) { eventVariables.add(variable) } } // filtering conditional and untriggered obligations and powers for (obligation : model.obligations) { if (obligation.trigger !== null) { untriggeredObligations.add(obligation) } else { triggeredObligations.add(obligation) } if (obligation.antecedent instanceof PAtomPredicateTrueLiteral) { unconditionalObligations.add(obligation) } else { conditionalObligations.add(obligation) } } for (obligation : model.survivingObligations) { if (obligation.trigger !== null) { untriggeredSurvivingObligations.add(obligation) } else { triggeredSurvivingObligations.add(obligation) } if (obligation.antecedent instanceof PAtomPredicateTrueLiteral) { unconditionalSurvivingObligations.add(obligation) } else { conditionalSurvivingObligations.add(obligation) } } for (power : model.powers) { if (power.trigger !== null) { untriggeredPowers.add(power) } else { triggeredPowers.add(power) } if (power.antecedent instanceof PAtomPredicateTrueLiteral) { unconditionalPowers.add(power) } else { conditionalPowers.add(power) } } allObligations.addAll(untriggeredObligations) allObligations.addAll(triggeredObligations) allSurvivingObligations.addAll(untriggeredSurvivingObligations) allSurvivingObligations.addAll(triggeredSurvivingObligations) allPowers.addAll(untriggeredPowers) allPowers.addAll(triggeredPowers) // collect trigger events for (obligation : untriggeredObligations) { val proposition = obligation.trigger val list = collectPropositionEvents(proposition) if (list.size > 0) { obligationTriggerEvents.put(obligation, list) } } for (obligation : untriggeredSurvivingObligations) { val proposition = obligation.trigger val list = collectPropositionEvents(proposition) if (list.size > 0) { survivingObligationTriggerEvents.put(obligation, list) } } for (power : untriggeredPowers) { val proposition = power.trigger val list = collectPropositionEvents(proposition) if (list.size > 0) { powerTriggerEvents.put(power, list) } } // collect fulfillment events of obligations for (obligation : allObligations) { val proposition = obligation.consequent val list = collectPropositionEvents(proposition) if (list.size > 0) { obligationFullfilmentEvents.put(obligation, list) } } for (obligation : allSurvivingObligations) { val proposition = obligation.consequent val list = collectPropositionEvents(proposition) if (list.size > 0) { survivingObligationFullfilmentEvents.put(obligation, list) } } // collect antecedent activates for (obligation : conditionalObligations) { val proposition = obligation.antecedent val list = collectPropositionEvents(proposition) if (list.size > 0) { obligationAntecedentEvents.put(obligation, list) } } for (obligation : conditionalSurvivingObligations) { val proposition = obligation.antecedent val list = collectPropositionEvents(proposition) if (list.size > 0) { survivingObligationAntecedentEvents.put(obligation, list) } } for (power : conditionalPowers) { val proposition = power.antecedent val list = collectPropositionEvents(proposition) if (list.size > 0) { powerAntecedentEvents.put(power, list) } } } def void generateNPMFile(IFileSystemAccess2 fsa, Model model) { val file = ''' { "name": "«model.contractName.toLowerCase»", "version": "1.0.0", "description": "", "main": "index.js", "engines": { "node": ">=14", "npm": ">=5" }, "scripts": { "lint": "eslint .", "pretest": "npm run lint", "test": "nyc mocha --recursive", "start": "fabric-chaincode-node start" }, "engineStrict": true, "author": "Symboleo2SC", "dependencies": { "fabric-contract-api": "^2.2.2", "fabric-shim": "^2.2.2", "symboleo-js-core": "^1.0.12" }, "devDependencies": { "chai": "^4.1.2", "eslint": "^8.7.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.25.4", "mocha": "^8.0.1", "nyc": "^14.1.1", "sinon": "^6.0.0", "sinon-chai": "^3.2.0" } } ''' fsa.generateFile("./" + model.contractName + "/package.json", file) } def String compileEventsMap() { // trigger events to instantiate // val arrays= new ArrayList for (obligation : obligationTriggerEvents.keySet) { arrays.add( generateEventMapLineString( obligationTriggerEvents.get(obligation), '''EventListeners.createObligation_«obligation.name»''')) } for (obligation : survivingObligationTriggerEvents.keySet) { arrays.add( generateEventMapLineString(survivingObligationTriggerEvents.get( obligation), '''EventListeners.createSurvivingObligation_«obligation.name»''')) } for (power : powerTriggerEvents.keySet) { arrays.add( generateEventMapLineString(powerTriggerEvents.get(power), '''EventListeners.createPower_«power.name»''')) } // antecedent events to activate for (obligation : obligationAntecedentEvents.keySet) { arrays.add( generateEventMapLineString( obligationAntecedentEvents.get(obligation), '''EventListeners.activateObligation_«obligation.name»''')) } for (obligation : survivingObligationAntecedentEvents.keySet) { arrays.add( generateEventMapLineString(survivingObligationAntecedentEvents.get( obligation), '''EventListeners.activateSurvivingObligation_«obligation.name»''')) } for (power : powerAntecedentEvents.keySet) { arrays.add( generateEventMapLineString(powerAntecedentEvents.get(power), '''EventListeners.activatePower_«power.name»''')) } // fulfill obligation events for (obligation : obligationFullfilmentEvents.keySet) { arrays.add( generateEventMapLineString( obligationFullfilmentEvents.get(obligation), '''EventListeners.fulfillObligation_«obligation.name»''')) } for (obligation : survivingObligationFullfilmentEvents.keySet) { arrays.add( generateEventMapLineString(survivingObligationFullfilmentEvents.get( obligation), '''EventListeners.fulfillSurvivingObligation_«obligation.name»''')) } // contract termination // for (obligation : allObligations) { // arrays. // add('''[[new InternalEvent(InternalEventSource.obligation, InternalEventType.obligation.Fulfilled, contract.obligations.«obligation.name»)], EventListeners.successfullyTerminateContract],''') // } // for (line :arrays) { // arraysEvents.add(line)} return ''' function getEventMap(contract) { return [ «FOR line : arrays» «line» «ENDFOR» ] } ''' } def void compileSerializerFile(IFileSystemAccess2 fsa, Model model) { val code = ''' const { «model.contractName» } = require("./domain/contract/«model.contractName».js") const { Obligation, ObligationActiveState, ObligationState } = require(«OBLIGATION_CLASS_IMPORT_PATH») const { InternalEventType, InternalEvent, InternalEventSource} = require(«EVENTS_CLASS_IMPORT_PATH») const { Event } = require(«EVENTS_CLASS_IMPORT_PATH») const { Power } = require(«POWER_CLASS_IMPORT_PATH») const { ContractState, ContractActiveState } = require(«CONTRACT_CLASS_IMPORT_PATH») const { Events } = require(«EVENTS_CLASS_IMPORT_PATH») const { EventListeners, getEventMap } = require("./events.js") function deserialize(data) { const object = JSON.parse(data) const contract = new «model.contractName»(«model.parameters.map[Parameter p | "object." + p.name].join(', ')») « FOR e : AssignVar » contract.«e»= object.«e» «ENDFOR» contract.state = object.state contract.activeState = object.activeState for (const eventType of Object.keys(InternalEventType.contract)) { if (object._events[eventType] != null) { const eventObject = new Event() eventObject._triggered = object._events[eventType]._triggered eventObject._timestamp = object._events[eventType]._timestamp contract._events[eventType] = eventObject } } for (const key of [«eventVariables.map[Variable v | "'" + v.name + "'"].join(', ')»]) { for(const eKey of Object.keys(object[key])) { contract[key][eKey] = object[key][eKey] } } «FOR obligation : allObligations» if (object.obligations.«obligation.name» != null) { const obligation = new Obligation('«obligation.name»', «generateDotExpressionString(obligation.creditor, "contract")», «generateDotExpressionString(obligation.debtor, "contract")», contract) obligation.state = object.obligations.«obligation.name».state obligation.activeState = object.obligations.«obligation.name».activeState obligation._createdPowerNames = object.obligations.«obligation.name»._createdPowerNames obligation._suspendedByContractSuspension = object.obligations.«obligation.name»._suspendedByContractSuspension for (const eventType of Object.keys(InternalEventType.obligation)) { if (object.obligations.«obligation.name»._events[eventType] != null) { const eventObject = new Event() eventObject._triggered = object.obligations.«obligation.name»._events[eventType]._triggered eventObject._timestamp = object.obligations.«obligation.name»._events[eventType]._timestamp obligation._events[eventType] = eventObject } } contract.obligations.«obligation.name» = obligation } «ENDFOR» «FOR obligation : allSurvivingObligations» if (object.survivingObligations.«obligation.name» != null) { const obligation = new Obligation('«obligation.name»', «generateDotExpressionString(obligation.creditor, "contract")», «generateDotExpressionString(obligation.debtor, "contract")», contract, true) obligation.state = object.survivingObligations.«obligation.name».state obligation.activeState = object.survivingObligations.«obligation.name».activeState obligation._createdPowerNames = object.survivingObligations.«obligation.name»._createdPowerNames obligation._suspendedByContractSuspension = object.survivingObligations.«obligation.name»._suspendedByContractSuspension for (const eventType of Object.keys(InternalEventType.obligation)) { if (object.survivingObligations.«obligation.name»._events[eventType] != null) { const eventObject = new Event() eventObject._triggered = object.survivingObligations.«obligation.name»._events[eventType]._triggered eventObject._timestamp = object.survivingObligations.«obligation.name»._events[eventType]._timestamp obligation._events[eventType] = eventObject } } contract.survivingObligations.«obligation.name» = obligation } «ENDFOR» «FOR power : allPowers» if (object.powers.«power.name» != null) { const power = new Power('«power.name»', «generateDotExpressionString(power.creditor, "contract")», «generateDotExpressionString(power.creditor, "contract")», contract) power.state = object.powers.«power.name».state power.activeState = object.powers.«power.name».activeState for (const eventType of Object.keys(InternalEventType.power)) { if (object.powers.«power.name»._events[eventType] != null) { const eventObject = new Event() eventObject._triggered = object.powers.«power.name»._events[eventType]._triggered eventObject._timestamp = object.powers.«power.name»._events[eventType]._timestamp power._events[eventType] = eventObject } } contract.powers.«power.name» = power } «ENDFOR» return contract } function serialize(contract) { for (const key of Object.keys(contract.obligations)){ contract.obligations[key].contract = undefined contract.obligations[key].creditor = undefined contract.obligations[key].debtor = undefined } for (const key of Object.keys(contract.powers)){ contract.powers[key].contract = undefined contract.powers[key].creditor = undefined contract.powers[key].debtor = undefined } for (const key of Object.keys(contract.survivingObligations)){ contract.survivingObligations[key].contract = undefined contract.survivingObligations[key].creditor = undefined contract.survivingObligations[key].debtor = undefined } return JSON.stringify(contract) } module.exports.deserialize = deserialize module.exports.serialize = serialize ''' fsa.generateFile("./" + model.contractName + "/" + "serializer.js", code) } def void compileEventsFile(IFileSystemAccess2 fsa, Model model) { val code = ''' const { InternalEventSource, InternalEvent, InternalEventType } = require(«EVENTS_CLASS_IMPORT_PATH») const { Obligation } = require(«OBLIGATION_CLASS_IMPORT_PATH») const { Power } = require(«POWER_CLASS_IMPORT_PATH») const { Predicates } = require(«PREDICATES_CLASS_IMPORT_PATH») const { Utils } = require(«UTILS_CLASS_IMPORT_PATH») const { Str } = require(«UTILS_CLASS_IMPORT_PATH») «FOR enumeration : enumerations» const { «enumeration.name» } = require("./domain/types/«enumeration.name».js") «ENDFOR» const EventListeners = { «FOR obligation : obligationTriggerEvents.keySet» createObligation_«obligation.name»(contract) { if («generatePropositionString(obligation.trigger)») { «"\n"+generatePropositionAssignString(obligation.trigger)» if (contract.obligations.«obligation.name» == null || contract.obligations.«obligation.name».isFinished()) { const isNewInstance = contract.obligations.«obligation.name» != null && contract.obligations.«obligation.name».isFinished() contract.obligations.«obligation.name» = new Obligation('«obligation.name»', «generateDotExpressionString(obligation.creditor, 'contract')», «generateDotExpressionString(obligation.debtor, 'contract')», contract) if («obligation.antecedent instanceof PAtomPredicateTrueLiteral ? "true" : "!isNewInstance "» ) { «"\n"+generatePropositionAssignString(obligation.antecedent)» contract.obligations.«obligation.name».trigerredUnconditional() if (!isNewInstance && «generatePropositionString(obligation.consequent)») { «"\n"+generatePropositionAssignString(obligation.consequent)» contract.obligations.«obligation.name».fulfilled() } } else { contract.obligations.«obligation.name».trigerredConditional() } } } }, «ENDFOR» «FOR obligation : survivingObligationTriggerEvents.keySet» createSurvivingObligation_«obligation.name»(contract) { if («generatePropositionString(obligation.trigger)») { «"\n"+generatePropositionAssignString(obligation.trigger)» if (contract.survivingObligations.«obligation.name» == null || contract.survivingObligations.«obligation.name».isFinished()) { contract.survivingObligations.«obligation.name» = new Obligation('«obligation.name»', «generateDotExpressionString(obligation.creditor, 'contract')», «generateDotExpressionString(obligation.debtor, 'contract')», contract, true) if («generatePropositionString(obligation.antecedent)») { «"\n"+generatePropositionAssignString(obligation.antecedent)» contract.survivingObligations.«obligation.name».trigerredUnconditional() «obligation.consequent instanceof PAtomPredicate ? "" : " if ( !isNewInstance &&" + generatePropositionString(obligation.antecedent)+")" » if («generatePropositionString(obligation.consequent)» ) { «"\n"+generatePropositionAssignString(obligation.consequent)» contract.survivingObligations.«obligation.name».fulfilled() } } else { contract.survivingObligations.«obligation.name».trigerredConditional() } } } }, «ENDFOR» «FOR power : powerTriggerEvents.keySet» createPower_«power.name»(contract) { const effects = { powerCreated: false } if («generatePropositionString(power.trigger)») { «"\n"+generatePropositionAssignString(power.trigger)» if (contract.powers.«power.name» == null || contract.powers.«power.name».isFinished()){ const isNewInstance = contract.powers.«power.name» != null && contract.powers.«power.name».isFinished() contract.powers.«power.name» = new Power('«power.name»', «generateDotExpressionString(power.creditor, 'contract')», «generateDotExpressionString(power.debtor, 'contract')», contract) effects.powerCreated = true effects.powerName = '«power.name»' if («power.antecedent instanceof PAtomPredicateTrueLiteral ? "true" : "!isNewInstance && "+generatePropositionString(power.antecedent) » ) { «"\n"+generatePropositionAssignString(power.antecedent)» contract.powers.«power.name».trigerredUnconditional() } else { contract.powers.«power.name».trigerredConditional() } } } return effects }, «ENDFOR» «FOR obligation : obligationAntecedentEvents.keySet» activateObligation_«obligation.name»(contract) { if (contract.obligations.«obligation.name» != null && («generatePropositionString(obligation.antecedent)»)) { «"\n"+generatePropositionAssignString(obligation.antecedent)» contract.obligations.«obligation.name».activated() if («generatePropositionString(obligation.consequent)») { «"\n"+generatePropositionAssignString(obligation.consequent)» contract.obligations.«obligation.name».fulfilled() } } }, «ENDFOR» «FOR obligation : survivingObligationAntecedentEvents.keySet» activateSurvivingObligation_«obligation.name»(contract) { if (contract.survivingObligations.«obligation.name» != null && («generatePropositionString(obligation.antecedent)») ) { «"\n"+generatePropositionAssignString(obligation.antecedent)» contract.survivingObligations.«obligation.name».activated() if («generatePropositionString(obligation.consequent)») { «"\n"+generatePropositionAssignString(obligation.consequent)» contract.survivingObligations.«obligation.name».fulfilled() } } }, «ENDFOR» «FOR power : powerAntecedentEvents.keySet» activatePower_«power.name»(contract) { if (contract.powers.«power.name» != null && («generatePropositionString(power.antecedent)»)) { «"\n"+generatePropositionAssignString(power.antecedent)» contract.powers.«power.name».activated() } }, «ENDFOR» «FOR obligation : obligationFullfilmentEvents.keySet» fulfillObligation_«obligation.name»(contract) { if (contract.obligations.«obligation.name» != null && («generatePropositionString(obligation.consequent)») ) { «"\n"+generatePropositionAssignString(obligation.consequent)» contract.obligations.«obligation.name».fulfilled() } }, «ENDFOR» «FOR obligation : survivingObligationFullfilmentEvents.keySet» fulfillSurvivingObligation_«obligation.name»(contract) { if (contract.survivingObligations.«obligation.name» != null && «generatePropositionString(obligation.consequent)» ) { «"\n"+generatePropositionAssignString(obligation.consequent)» contract.survivingObligations.«obligation.name».fulfilled() } }, «ENDFOR» successfullyTerminateContract(contract) { for (const oblKey of Object.keys(contract.obligations)) { if (contract.obligations[oblKey].isActive()) { return; } if (contract.obligations[oblKey].isViolated() && Array.isArray(contract.obligations[oblKey]._createdPowerNames)) { for (const pKey of contract.obligations[oblKey]._createdPowerNames) { if (!contract.powers[pKey].isSuccessfulTermination()) { return; } } } } contract.fulfilledActiveObligations() }, unsuccessfullyTerminateContract(contract) { for (let index in contract.obligations) { contract.obligations[index].terminated({emitEvent: false}) } for (let index in contract.powers) { contract.powers[index].terminated() } contract.terminated() } } «compileEventsMap()» module.exports.EventListeners = EventListeners module.exports.getEventMap = getEventMap ''' fsa.generateFile("./" + model.contractName + "/" + "events.js", code) } def String generateEventMapLineString(List predicates, String listenerName) { val line = new StringBuilder() line.append('[[') for (predicate : predicates) { val pf = predicate.predicateFunction switch (pf) { PredicateFunctionHappens: line.append(generateInternalEventObjectString(pf.event) + ', ') PredicateFunctionWHappensBefore: line.append(generateInternalEventObjectString(pf.event) + ', ') PredicateFunctionHappensAfter: line.append(generateInternalEventObjectString(pf.event) + ', ') PredicateFunctionSHappensBefore: { line.append(generateInternalEventObjectString(pf.event) + ', ') val res = generatePointInternalEventObjectString(pf.point.pointExpression) if (res !== null){ line.append(res + ', ') } } PredicateFunctionHappensWithin: { line.append(generateInternalEventObjectString(pf.event) + ', ') val interval = pf.interval.intervalExpression switch(interval){ IntervalFunction: { val res1 = generatePointInternalEventObjectString(interval.arg1) val res2 = generatePointInternalEventObjectString(interval.arg2) if (res1 !== null) { line.append(res1 + ', ') } if (res2 !== null) { line.append(res2 + ', ') } } } } PredicateFunctionAssignment: { line.append(generateInternalEventObjectString(pf.event) + ', ') //line.append(generateAssignObjectString(pf.assignment) + ',') } // To write in the getEventMap. writing delivered/event before the assignment expression only } } if (line.toString() !='[['){ line.append('''], «listenerName»],''') return line.toString } else return "" } def String generateInternalEventObjectString(Event event) { switch (event) { VariableEvent: return '''new InternalEvent(InternalEventSource.contractEvent, InternalEventType.contractEvent.Happened, «generateDotExpressionString(event.variable, 'contract')»)''' ObligationEvent: return '''new InternalEvent(InternalEventSource.obligation, InternalEventType.obligation.«event.eventName», contract.«isSurvivingObligation(event.obligationVariable.name) ? "survivingObligations" : "obligations"».«event.obligationVariable.name»)''' ContractEvent: return '''new InternalEvent(InternalEventSource.contract, InternalEventType.contract.«event.eventName», contract)''' PowerEvent: return '''new InternalEvent(InternalEventSource.power, InternalEventType.power.«event.eventName», contract.powers.«event.powerVariable.name»)''' } } /* def String generateAssignObjectString(Assignment a) { var s = "" if (a instanceof AssignExpression) s= "contract."+ a.name + " = " + generateExpressionString(a.value, 'contract') return s }*/ def String generateOAssignObjectString(List a) { var s = "" var eName="" var found=false for(e: a){ found=false if (e instanceof OAssignExpression){ eName=generateDotExpressionString(e.name2,"" ) if (!(AssignVar.contains(eName) )) { for( p : parameters){ if (p.name.toString()==eName.toString()){found=true} } if (!(found)){AssignVar.add(eName) } } s= s+generateDotExpressionString(e.name2,"contract" )+ " = " + generateExpressionString(e.value, 'contract') // s= generateExpressionString(a.name, 'contract' )+ OpString(a.op) + generateExpressionString(a.value, 'contract') s=s+" \n" } } return s } def String generatePointInternalEventObjectString(PointExpression p) { switch (p) { PointFunction: { val res = generatePointInternalEventObjectString(p.arg) if(res !== null) { return res } else { return null } } PointAtomParameterDotExpression: { if(Helpers.isDotExpressionTypeOfEvent(p.variable, variables, parameters)) { return '''new InternalEvent(InternalEventSource.contractEvent, InternalEventType.contractEvent.Happened, «generateDotExpressionString(p.variable, 'contract')»)''' } else { return null } } PointAtomObligationEvent: { val e = p.obligationEvent as ObligationEvent return '''new InternalEvent(InternalEventSource.obligation, InternalEventType.obligation.«e.eventName», contract.«isSurvivingObligation(e.obligationVariable.name) ? "survivingObligations" : "obligations"».«e.obligationVariable.name»)''' } PointAtomContractEvent: { val e = p.contractEvent as ContractEvent return '''new InternalEvent(InternalEventSource.contract, InternalEventType.contract.«e.eventName», contract)''' } PointAtomPowerEvent: { val e = p.powerEvent as PowerEvent return '''new InternalEvent(InternalEventSource.power, InternalEventType.power.«e.eventName», contract.powers.«e.powerVariable.name»)''' } } } private def boolean isSurvivingObligation (String name) { for (obligation: allObligations){ if(obligation.name.equals(name)){ return false } } for (obligation: allSurvivingObligations){ if(obligation.name.equals(name)){ return true } } } def List collectPropositionEvents(Proposition proposition) { val list = new ArrayList switch (proposition) { POr: { list.addAll(collectPropositionEvents(proposition.left)) list.addAll(collectPropositionEvents(proposition.right)) } PAnd: { list.addAll(collectPropositionEvents(proposition.left)) list.addAll(collectPropositionEvents(proposition.right)) } PEquality: { list.addAll(collectPropositionEvents(proposition.left)) list.addAll(collectPropositionEvents(proposition.right)) } PComparison: { list.addAll(collectPropositionEvents(proposition.left)) list.addAll(collectPropositionEvents(proposition.right)) } PAtomRecursive: list.addAll(collectPropositionEvents(proposition.inner)) NegatedPAtom: list.addAll(collectPropositionEvents(proposition.negated)) PAtomPredicate:{ if (!(proposition.predicateFunction instanceof PredicateFunctionAssignmentOnly)){ list.add(proposition)} } } return list } def String generatePropositionString(Proposition proposition) { switch (proposition) { POr: return generatePropositionString(proposition.left) + " || " + generatePropositionString(proposition.right) PAnd: return generatePropositionString(proposition.left) + " && " + generatePropositionString(proposition.right) PEquality: return generatePropositionString(proposition.left) + getEqualityOperator(proposition.op) + generatePropositionString(proposition.right) PComparison: return generatePropositionString(proposition.left) + ' ' + proposition.op + ' ' + generatePropositionString(proposition.right) PAtomRecursive: return "(" + generatePropositionString(proposition.inner) + ")" NegatedPAtom: return "!(" + generatePropositionString(proposition.negated) + ")" PAtomPredicate: return generatePredicateFunctionString(proposition.predicateFunction) PAtomEnum: return proposition.enumeration.name + "." + proposition.enumItem.name PAtomVariable: return generateDotExpressionString(proposition.variable, 'contract') PAtomPredicateTrueLiteral: return "true" PAtomPredicateFalseLiteral: return "false" PAtomDoubleLiteral: return proposition.value.toString PAtomIntLiteral: return proposition.value.toString PAtomDateLiteral: return '''(new Date("«proposition.value.toInstant.toString»").toISOString())''' PAtomStringLiteral: return proposition.value } } def String generatePropositionAssignString(Proposition proposition) { switch (proposition) { POr: return generatePropositionAssignString(proposition.left) + generatePropositionAssignString(proposition.right) PAnd: return generatePropositionAssignString(proposition.left) + generatePropositionAssignString(proposition.right) PEquality: return generatePropositionAssignString(proposition.left) + generatePropositionAssignString(proposition.right) PComparison: return generatePropositionAssignString(proposition.left) + generatePropositionAssignString(proposition.right) PAtomRecursive: return generatePropositionAssignString(proposition.inner) NegatedPAtom: return generatePropositionAssignString(proposition.negated) PAtomPredicate: return generatePredicateAssignString(proposition.predicateFunction) default : return " " } } def String generatePredicateFunctionString(PredicateFunction predicate) { switch (predicate) { PredicateFunctionHappens: return '''Predicates.happens(«generateEventVariableString(predicate.event)») ''' PredicateFunctionHappensAfter: return '''Predicates.happensAfter(«generateEventVariableString(predicate.event)», «generatePointExpresionString(predicate.point.pointExpression)»)''' PredicateFunctionWHappensBefore: return '''Predicates.weakHappensBefore(«generateEventVariableString(predicate.event)», «generatePointExpresionString(predicate.point.pointExpression)») ''' PredicateFunctionSHappensBefore: return '''Predicates.strongHappensBefore(«generateEventVariableString(predicate.event)», «generatePointExpresionString(predicate.point.pointExpression)») ''' PredicateFunctionHappensWithin: return '''Predicates.happensWithin(«generateEventVariableString(predicate.event)», «generateIntervalExpresionArgString(predicate.interval.intervalExpression)») ''' PredicateFunctionAssignment: return '''Predicates.happens(«generateEventVariableString(predicate.event)») ''' PredicateFunctionAssignmentOnly: return '''true''' } } def String generatePredicateAssignString(PredicateFunction predicate) { switch (predicate) { PredicateFunctionAssignment: return ''' «generateOAssignObjectString(predicate.assignment)» ''' PredicateFunctionAssignmentOnly: return ''' «generateOAssignObjectString(predicate.assignment)» ''' default : return " " } } def String generateEventVariableString(Event event) { switch (event) { VariableEvent: return generateDotExpressionString(event.variable, 'contract') PowerEvent: return '''contract.powers.«event.powerVariable.name» && contract.powers.«event.powerVariable.name»._events.«event.eventName»''' ObligationEvent: return '''contract.«isSurvivingObligation(event.obligationVariable.name) ? "survivingObligations" : "obligations"».«event.obligationVariable.name» && contract.«isSurvivingObligation(event.obligationVariable.name) ? "survivingObligations" : "obligations"».«event.obligationVariable.name»._events.«event.eventName»''' ContractEvent: return '''contract._events.«event.eventName»''' } } def String generatePointExpresionString(PointExpression point) { switch (point) { PointFunction: return '''Utils.addTime(«generatePointExpresionString(point.arg)», «generateTimeValueString(point.value)», "«point.timeUnit»")''' PointAtomParameterDotExpression: { if (Helpers.isDotExpressionTypeOfEvent(point.variable, variables, parameters)) { return '''«generateDotExpressionString(point.variable, 'contract')»._timestamp''' } else { return generateDotExpressionString(point.variable, 'contract') } } PointAtomObligationEvent: { val e = point.obligationEvent as ObligationEvent val obligationRef = isSurvivingObligation(e.obligationVariable.name) ? "survivingObligations" : "obligations" return '''contract.«obligationRef».«e.obligationVariable.name» && contract.«obligationRef».«e.obligationVariable.name»._events.«e.eventName» && contract.«obligationRef».«e.obligationVariable.name»._events.«e.eventName»._timestamp''' } PointAtomPowerEvent: { val e = point.powerEvent as PowerEvent return '''contract.powers.«e.powerVariable.name» && contract.powers.«e.powerVariable.name»._events.«e.eventName» && contract.powers.«e.powerVariable.name»._events.«e.eventName»._timestamp''' } PointAtomContractEvent: { val e = point.contractEvent as ContractEvent return '''contract._events.«e.eventName» && contract._events.«e.eventName»._timestamp''' } } } def String generateTimeValueString(Timevalue tv) { switch (tv) { TimevalueInt: return tv.value.toString TimevalueVariable: return generateDotExpressionString(tv.variable, 'contract') } } def String generateIntervalExpresionArgString(IntervalExpression interval) { switch (interval) { IntervalFunction: return '''«generatePointExpresionString(interval.arg1)», «generatePointExpresionString(interval.arg2)»''' SituationExpression: { val situation = interval.situation switch (situation) { ObligationState: return '''contract.«isSurvivingObligation(situation.obligationVariable.name) ? "survivingObligations" : "obligations"».«situation.obligationVariable.name», "Obligation.«situation.stateName»"''' PowerState: return '''contract.powers.«situation.powerVariable.name», "Power.«situation.stateName»""''' ContractState: return '''contract, "Contract.«situation.stateName»"''' } } } } def void compileTransactionFile(IFileSystemAccess2 fsa, Model model) { val code = ''' const { Contract } = require("fabric-contract-api") const { «model.contractName» } = require("./domain/contract/«model.contractName».js") const { deserialize, serialize } = require("./serializer.js") const { Events } = require(«EVENT_CLASS_IMPORT_PATH») const { InternalEvent, InternalEventSource, InternalEventType } = require(«EVENT_CLASS_IMPORT_PATH») const { getEventMap, EventListeners } = require("./events.js") ««« «FOR asset : assets» ««« import { «asset.name» } = require( "./domain/assets/«asset.name»") ««« «ENDFOR» ««« «FOR event : events» ««« const { «event.name» } = require( "./domain/events/«event.name»") ««« «ENDFOR» ««« «FOR role : roles» ««« const { «role.name» } = require( "./domain/roles/«role.name»") ««« «ENDFOR» ««« «FOR enumeration : enumerations» ««« const { «enumeration.name» } = require( "./domain/types/«enumeration.name»") ««« «ENDFOR» class HFContract extends Contract { constructor() { super('«model.contractName»'); } initialize(contract) { Events.init(getEventMap(contract), EventListeners) } «compileInitMethod(model)» «FOR method : compileEventTriggerMethods(model)» «method» «ENDFOR» «FOR method : compilePowerTransactions(model)» «method» «ENDFOR» «FOR method : compileViolationEventsTransactions(model)» «method» «ENDFOR» «FOR method : compileExpirationTransactions(model)» «method» «ENDFOR» «FOR method : compilePowerExpirationTransactions(model)» «method» «ENDFOR» async getState(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) let output = `Contract state: ${contract.state}-${contract.activeState}\r\n` output += 'Obligations:\r\n' for (const obligationKey of Object.keys(contract.obligations)) { output += ` ${obligationKey}: ${contract.obligations[obligationKey].state}-${contract.obligations[obligationKey].activeState}\r\n` } output += 'Powers:\r\n' for (const powerKey of Object.keys(contract.powers)) { output += ` ${powerKey}: ${contract.powers[powerKey].state}-${contract.powers[powerKey].activeState}\r\n` } output += 'Surviving Obligations:\r\n' for (const obligationKey of Object.keys(contract.survivingObligations)) { output += ` ${obligationKey}: ${contract.survivingObligations[obligationKey].state}-${contract.survivingObligations[obligationKey].activeState}\r\n` } output += 'Events:\r\n' «FOR event : eventVariables» if (contract.«event.name»._triggered) { output += ` Event "«event.name»" happened at ${contract.«event.name»._timestamp}\r\n` } else { output += ` Event "«event.name»" has not happened\r\n` } «ENDFOR» return output } } module.exports.contracts = [HFContract]; ''' fsa.generateFile("./" + model.contractName + "/" + "index.js", code) } def List compileViolationEventsTransactions(Model model) { val methods = new ArrayList for (obligation : allObligations) { methods.add(''' async violateObligation_«obligation.name»(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect()) { if (contract.obligations.«obligation.name» != null && contract.obligations.«obligation.name».violated()) { await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } } ''') } // we added | contract.isSuccessfulTermination() for (obligation : allSurvivingObligations) { methods.add(''' async violateSurvivingObligations_«obligation.name»(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect() || contract.isSuccessfulTermination()) { if (contract.survivingObligations.«obligation.name» != null && contract.survivingObligations.«obligation.name».violated()) { await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } } ''') } return methods } def List compileExpirationTransactions(Model model) { val methods = new ArrayList for (obligation : conditionalObligations) { methods.add(''' async expireObligation_«obligation.name»(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect()) { if (contract.obligations.«obligation.name» != null && contract.obligations.«obligation.name».expired()) { await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } } ''') } // New, it is added to update the state of the surviving obligations for (obligation : conditionalSurvivingObligations) { methods.add(''' async expireSurvivingObligation_«obligation.name»(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect() || contract.isSuccessfulTermination()) { if (contract.survivingObligations.«obligation.name» != null && contract.survivingObligations.«obligation.name».expired()) { await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } } ''') } return methods } def List compilePowerExpirationTransactions(Model model) { val methods = new ArrayList for (power : allPowers) { methods.add(''' async expirePower_«power.name»(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect()) { if (contract.powers.«power.name» != null && contract.powers.«power.name».expired()) { await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } } ''') } return methods } def List compilePowerTransactions(Model model) { val methods = new ArrayList for (power : model.powers) { val powerFunction = power.consequent switch (powerFunction) { PFObligationSuspended: methods.add( generatePowerTransactionForObligation(power.name, powerFunction.norm.name, 'suspended')) PFObligationResumed: methods.add( generatePowerTransactionForObligation(power.name, powerFunction.norm.name, 'resumed')) PFObligationDischarged: methods.add( generatePowerTransactionForObligation(power.name, powerFunction.norm.name, 'discharged')) PFObligationTerminated: methods.add( generatePowerTransactionForObligation(power.name, powerFunction.norm.name, 'terminated')) PFObligationTriggered: methods.add( generatePowerTransactionForObligation(power.name, powerFunction.norm.name, 'triggered')) PFContractSuspended: methods.add(generatePowerTransactionForContract(power.name, 'suspended')) PFContractResumed: methods.add(generatePowerTransactionForContract(power.name, 'resumed')) PFContractTerminated: methods.add(generatePowerTransactionForContract(power.name, 'terminated')) } } return methods } def String generatePowerTransactionForObligation(String powerName, String obligationName, String stateMethod) { return ''' async p_«powerName»_«stateMethod»_o_«obligationName»(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect() && contract.powers.«powerName» != null && contract.powers.«powerName».isInEffect()) { «IF stateMethod.equals("triggered")» if (contract.powers.«powerName».exerted()) { «ELSE» const obligation = contract.«isSurvivingObligation(obligationName) ? "survivingObligations" : "obligations"».«obligationName» if (obligation != null && obligation.«stateMethod»() && contract.powers.«powerName».exerted()) { «ENDIF» await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } }''' } def String generatePowerTransactionForContract(String powerName, String stateMethod) { return ''' async p_«powerName»_«stateMethod»_contract(ctx, contractId) { const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect() && contract.powers.«powerName» != null && contract.powers.«powerName».isInEffect()) { for (let index in contract.obligations) { const obligation = contract.obligations[index] «IF stateMethod.equals("suspended")» obligation._suspendedByContractSuspension = true obligation.suspended() «ELSEIF stateMethod.equals("resumed")» if (obligation._suspendedByContractSuspension === true){ obligation.resumed() } «ELSEIF stateMethod.equals("terminated")» obligation.terminated({emitEvent: false}) «ENDIF» } for (let index in contract.survivingObligations) { const obligation = contract.survivingObligations[index] «IF stateMethod.equals("suspended")» obligation._suspendedByContractSuspension = true obligation.suspended() «ELSEIF stateMethod.equals("resumed")» if (obligation._suspendedByContractSuspension === true){ obligation.resumed() } «ELSEIF stateMethod.equals("terminated")» obligation.terminated() «ENDIF» } for (let index in contract.powers) { const power = contract.powers[index] if (index === '«powerName»') { continue; } «IF stateMethod.equals("suspended")» power._suspendedByContractSuspension = true power.suspended() «ELSEIF stateMethod.equals("resumed")» if (power._suspendedByContractSuspension === true){ power.resumed() } «ELSEIF stateMethod.equals("terminated")» power.terminated() «ENDIF» } if (contract.«stateMethod»() && contract.powers.«powerName».exerted()) { await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } else { return {successful: false} } }''' } def List compileEventTriggerMethods(Model model) { val methods = new ArrayList for (variable : eventVariables) { methods.add(''' async trigger_«variable.name»(ctx, args) { const inputs = JSON.parse(args); const contractId = inputs.contractId; const event = inputs.event; const contractState = await ctx.stub.getState(contractId) if (contractState == null) { return {successful: false} } const contract = deserialize(contractState.toString()) this.initialize(contract) if (contract.isInEffect() «survivEvent(variable.name)» ){ contract.«variable.name».happen(event) Events.emitEvent(contract, new InternalEvent(InternalEventSource.contractEvent, InternalEventType.contractEvent.Happened, contract.«variable.name»)) await ctx.stub.putState(contractId, Buffer.from(serialize(contract))) return {successful: true} } else { return {successful: false} } } ''') } return methods } def String compileInitMethod(Model model) { val code = ''' async init(ctx, args) { const inputs = JSON.parse(args); const contractInstance = new «model.contractName» («model.parameters.map[Parameter p | "inputs." + p.name].join(', ')») this.initialize(contractInstance) if (contractInstance.activated()) { // call trigger transitions for legal positions «FOR obligation : triggeredObligations» «IF obligation.antecedent instanceof PAtomPredicateTrueLiteral» contractInstance.obligations.«obligation.name».trigerredUnconditional() «ELSE» contractInstance.obligations.«obligation.name».trigerredConditional() «ENDIF» «ENDFOR» «FOR obligation : triggeredSurvivingObligations» «IF obligation.antecedent instanceof PAtomPredicateTrueLiteral» contractInstance.survivingObligations.«obligation.name».trigerredUnconditional() «ELSE» contractInstance.survivingObligations.«obligation.name».trigerredConditional() «ENDIF» «ENDFOR» «FOR power : triggeredPowers» «IF power.antecedent instanceof PAtomPredicateTrueLiteral» contractInstance.powers.«power.name».trigerredUnconditional() «ELSE» contractInstance.powers.«power.name».trigerredConditional() «ENDIF» «ENDFOR» await ctx.stub.putState(contractInstance.id, Buffer.from(serialize(contractInstance))) return {successful: true, contractId: contractInstance.id} } else { return {successful: false} } } ''' return code } def void compileContract(IFileSystemAccess2 fsa, Model model) { val code = ''' «FOR asset : assets» const { «asset.name» } = require("../assets/«asset.name».js") «ENDFOR» «FOR event : events» const { «event.name» } = require("../events/«event.name».js") «ENDFOR» «FOR role : roles» const { «role.name» } = require("../roles/«role.name».js") «ENDFOR» «FOR enumeration : enumerations» const { «enumeration.name» } = require("../types/«enumeration.name».js") «ENDFOR» const { SymboleoContract } = require(«CONTRACT_CLASS_IMPORT_PATH») const { Obligation } = require(«OBLIGATION_CLASS_IMPORT_PATH») const { Power } = require(«POWER_CLASS_IMPORT_PATH») const { Utils } = require(«UTILS_CLASS_IMPORT_PATH») const { Str } = require(«UTILS_CLASS_IMPORT_PATH») class «model.contractName» extends SymboleoContract { constructor(«model.parameters.map[Parameter p | p.name].join(', ')») { super("«model.contractName»") this._name = "«model.contractName»" «FOR parameter : model.parameters» this.«parameter.name» = «parameter.name» «ENDFOR» this.obligations = {}; this.survivingObligations = {}; this.powers = {}; // assign varaibles of the contract «FOR variable : model.variables» «IF variable.type instanceof RegularType» this.«variable.name» = new «variable.type.name»("«variable.name»") «FOR assignment: variable.attributes» «IF assignment instanceof AssignExpression» this.«variable.name».«assignment.name» = «generateExpressionString(assignment.value, 'this')» «ENDIF» «ENDFOR» «ENDIF» «ENDFOR» // create instance of triggered obligations «FOR obligation : triggeredObligations» this.obligations.«obligation.name» = new Obligation('«obligation.name»', «generateDotExpressionString(obligation.creditor, 'this')», «generateDotExpressionString(obligation.debtor, 'this')», this) «ENDFOR» «FOR obligation : triggeredSurvivingObligations» this.survivingObligations.«obligation.name» = new Obligation('«obligation.name»', «generateDotExpressionString(obligation.creditor, 'this')», «generateDotExpressionString(obligation.debtor, 'this')», this, true) «ENDFOR» «FOR power : triggeredPowers» this.powers.«power.name» = new Power('«power.name»', «generateDotExpressionString(power.creditor, 'this')», «generateDotExpressionString(power.debtor, 'this')», this) «ENDFOR» } } module.exports.«model.contractName» = «model.contractName» ''' fsa.generateFile("./" + model.contractName + "/domain/contract/" + model.contractName + ".js", code) } def String generateExpressionString(Expression argExpression, String thisString) { switch (argExpression) { Or: return generateExpressionString(argExpression.left, thisString) + " || " + generateExpressionString(argExpression.right, thisString) And: return generateExpressionString(argExpression.left, thisString) + " && " + generateExpressionString(argExpression.right, thisString) Equality: return generateExpressionString(argExpression.left, thisString) + getEqualityOperator(argExpression.op) + generateExpressionString(argExpression.right, thisString) Comparison: return generateExpressionString(argExpression.left, thisString) + argExpression.op + generateExpressionString(argExpression.right, thisString) Plus: return generateExpressionString(argExpression.left, thisString) + " + " + generateExpressionString(argExpression.right, thisString) Minus: return generateExpressionString(argExpression.left, thisString) + " - " + generateExpressionString(argExpression.right, thisString) Multi: return generateExpressionString(argExpression.left, thisString) + " * " + generateExpressionString(argExpression.right, thisString) Div: return generateExpressionString(argExpression.left, thisString) + " / " + generateExpressionString(argExpression.right, thisString) PrimaryExpressionRecursive: return "(" + generateExpressionString(argExpression.inner, thisString) + ")" PrimaryExpressionFunctionCall: return generateFunctionCall(argExpression, thisString) NegatedPrimaryExpression: return "!(" + generateExpressionString(argExpression.expression, thisString) + ")" AtomicExpressionTrue: return "true" AtomicExpressionFalse: return "false" AtomicExpressionDouble: return argExpression.value.toString() AtomicExpressionInt: return argExpression.value.toString() AtomicExpressionDate: return '''(new Date("«argExpression.value.toInstant.toString»").toISOString())''' AtomicExpressionEnum: return argExpression.enumeration + "." + argExpression.enumItem AtomicExpressionString: return '"' + argExpression.value + '"' AtomicExpressionParameter: return generateDotExpressionString(argExpression.value, thisString) } } def String generateDotExpressionString(Ref argRef, String thisString) { val ids = new ArrayList() var ref = argRef while (ref instanceof VariableDotExpression) { ids.add(ref.tail.name) ref = ref.ref } if (ref instanceof VariableRef) { ids.add((ref as VariableRef).variable) } if (!(thisString==="")) {ids.add(thisString)} return ids.reverse().join(".") } def String generateFunctionCall(PrimaryExpressionFunctionCall argFunctionCallExp, String thisString) { val functionCall = argFunctionCallExp.function switch (functionCall) { TwoArgMathFunction: return functionCall.name + "(" + generateExpressionString(functionCall.arg1, thisString) + "," + generateExpressionString(functionCall.arg2, thisString) + ")" OneArgMathFunction: return functionCall.name + "(" + generateExpressionString(functionCall.arg1, thisString) + ")" ThreeArgStringFunction: return functionCall.name.replace("String", "Str") + "(" + generateExpressionString(functionCall.arg1, thisString) + "," + generateExpressionString(functionCall.arg2, thisString) + "," + generateExpressionString(functionCall.arg3, thisString) + ")" TwoArgStringFunction: return functionCall.name.replace("String", "Str") + "(" + generateExpressionString(functionCall.arg1, thisString) + "," + generateExpressionString(functionCall.arg2, thisString) + ")" OneArgStringFunction: return functionCall.name.replace("String", "Str") + "(" + generateExpressionString(functionCall.arg1, thisString) + ")" ThreeArgDateFunction: return '''Utils.addTime(«generateExpressionString(functionCall.arg1, thisString)», «generateExpressionString(functionCall.value, thisString)», "«functionCall.timeUnit»")''' // TwoArgUserFunction: // return functionCall.name + "(" + functionCall.arg1.toString() + "," + // functionCall.arg2.toString() + ")" } } def String getEqualityOperator(String op) { switch (op) { case '!=': return ' !== ' case '==': return ' === ' } } def void compileDomainTypes(IFileSystemAccess2 fsa, Model model) { for (asset : assets) { generateAsset(fsa, model, asset) } for (event : events) { generateEvent(fsa, model, event) } for (role : roles) { generateRole(fsa, model, role) } for (enumeration : enumerations) { generateEnumeration(fsa, model, enumeration) } } def void generateEnumeration(IFileSystemAccess2 fsa, Model model, Enumeration enumeration) { val code = ''' module.exports.«enumeration.name» = { «FOR item : enumeration.enumerationItems» «item.name»: «enumeration.enumerationItems.indexOf(item)», «ENDFOR» } ''' fsa.generateFile("./" + model.contractName + "/domain/types/" + enumeration.name + ".js", code) } def void generateAsset(IFileSystemAccess2 fsa, Model model, RegularType asset) { val isBase = asset.ontologyType !== null if (isBase === true) { val code = ''' const { Asset } = require(«ASSET_CLASS_IMPORT_PATH»); class «asset.name» extends Asset { constructor(_name,«asset.attributes.map[Attribute a | a.name].join(', ')») { super() this._name = _name «FOR attribute : asset.attributes» this.«attribute.name» = «attribute.name» «ENDFOR» } } module.exports.«asset.name» = «asset.name» ''' fsa.generateFile("./" + model.contractName + "/domain/assets/" + asset.name + ".js", code) } else if (asset.regularType !== null) { val parentType = asset.regularType val allAttributes = Helpers.getAttributesOfRegularType(asset) val parentAttributes = new ArrayList(allAttributes) parentAttributes.removeAll(asset.attributes) val code = ''' const { «parentType.name» } = require("./«parentType.name».js"); class «asset.name» extends «parentType.name» { constructor(_name,«allAttributes.map[Attribute a | a.name].join(', ')») { super(_name,«parentAttributes.map[Attribute a | a.name].join(', ')») «FOR attribute : asset.attributes» this.«attribute.name» = «attribute.name» «ENDFOR» } } module.exports.«asset.name» = «asset.name» ''' fsa.generateFile("./" + model.contractName + "/domain/assets/" + asset.name + ".js", code) } } def void generateEvent(IFileSystemAccess2 fsa, Model model, RegularType event) { val isBase = event.ontologyType !== null if (isBase === true) { val code = ''' const { Event } = require(«EVENT_CLASS_IMPORT_PATH»); class «event.name» extends Event { constructor(_name,«event.attributes.map[Attribute a | a.name].join(', ')») { super() this._name = _name «FOR attribute : event.attributes» this.«attribute.name» = «attribute.name» «ENDFOR» } } module.exports.«event.name» = «event.name» ''' fsa.generateFile("./" + model.contractName + "/domain/events/" + event.name + ".js", code) } else if (event.regularType !== null) { val parentType = event.regularType val allAttributes = Helpers.getAttributesOfRegularType(event) val parentAttributes = new ArrayList(allAttributes) parentAttributes.removeAll(event.attributes) val code = ''' const { «parentType.name» } = require("./«parentType.name».js"); class «event.name» extends «parentType.name» { constructor(_name,«allAttributes.map[Attribute a | a.name].join(', ')») { super(_name,«parentAttributes.map[Attribute a | a.name].join(', ')») «FOR attribute : event.attributes» this.«attribute.name» = «attribute.name» «ENDFOR» } } module.exports.«event.name» = «event.name» ''' fsa.generateFile("./" + model.contractName + "/domain/events/" + event.name + ".js", code) } } def void generateRole(IFileSystemAccess2 fsa, Model model, RegularType role) { val isBase = role.ontologyType !== null if (isBase === true) { val code = ''' const { Role } = require(«ROLE_CLASS_IMPORT_PATH»); class «role.name» extends Role { constructor(_name,«role.attributes.map[Attribute a | a.name].join(', ')») { super() this._name = _name «FOR attribute : role.attributes» this.«attribute.name» = «attribute.name» «ENDFOR» } } module.exports.«role.name» = «role.name» ''' fsa.generateFile("./" + model.contractName + "/domain/roles/" + role.name + ".js", code) } else if (role.regularType !== null) { val parentType = role.regularType val allAttributes = Helpers.getAttributesOfRegularType(role) val parentAttributes = new ArrayList(allAttributes) parentAttributes.removeAll(role.attributes) val code = ''' const { «parentType.name» } = require("./«parentType.name».js"); class «role.name» extends «parentType.name» { constructor(_name,«allAttributes.map[Attribute a | a.name].join(', ')») { super(_name,«parentAttributes.map[Attribute a | a.name].join(', ')») «FOR attribute : role.attributes» this.«attribute.name» = «attribute.name» «ENDFOR» } } module.exports.«role.name» = «role.name» ''' fsa.generateFile("./" + model.contractName + "/domain/roles/" + role.name + ".js", code) } } override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { for (e : resource.allContents.toIterable.filter(Model)) { assets.clear() events.clear() roles.clear() enumerations.clear() parameters.clear() arrays.clear() conditionalObligations.clear() conditionalSurvivingObligations.clear() conditionalPowers.clear() unconditionalObligations.clear() unconditionalSurvivingObligations.clear() unconditionalPowers.clear() untriggeredObligations.clear() untriggeredSurvivingObligations.clear() untriggeredPowers.clear() triggeredObligations.clear() triggeredSurvivingObligations.clear() triggeredPowers.clear() allObligations.clear() allSurvivingObligations.clear() allPowers.clear() eventVariables.clear() AssignVar.clear() obligationTriggerEvents.clear() survivingObligationTriggerEvents.clear() powerTriggerEvents.clear() obligationAntecedentEvents.clear() survivingObligationAntecedentEvents.clear() powerAntecedentEvents.clear() obligationFullfilmentEvents.clear() survivingObligationFullfilmentEvents.clear() System.out.println('generateHFSource: ' + e.contractName) generateHFSource(fsa, e) } } override void afterGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { assets.clear() events.clear() roles.clear() enumerations.clear() parameters.clear() AssignVar.clear() conditionalObligations.clear() conditionalSurvivingObligations.clear() conditionalPowers.clear() unconditionalObligations.clear() unconditionalSurvivingObligations.clear() unconditionalPowers.clear() // arrays.clear() untriggeredObligations.clear() untriggeredSurvivingObligations.clear() untriggeredPowers.clear() triggeredObligations.clear() triggeredSurvivingObligations.clear() triggeredPowers.clear() allObligations.clear() allSurvivingObligations.clear() allPowers.clear() eventVariables.clear() obligationTriggerEvents.clear() survivingObligationTriggerEvents.clear() powerTriggerEvents.clear() obligationAntecedentEvents.clear() survivingObligationAntecedentEvents.clear() powerAntecedentEvents.clear() obligationFullfilmentEvents.clear() survivingObligationFullfilmentEvents.clear() } def String OpString(String op) { switch (op) { case ":=": return ' = ' case "+:=": return " += " case "-:=": return " -= " case "*:=": return " *= " case "/:=": return " /= " } } def String survivEvent(String e ){ var survive=false var related=false for(line: arrays){ if (line.contains("contract."+e) && (line.contains('EventListeners.fulfillSurvivingObligation') || line.contains("EventListeners.createSurvivingObligation") || line.contains("EventListeners.activateSurvivingObligation") )) { survive=true } if (line.contains("contract."+e) && (line.contains('EventListeners.fulfillObligation') || line.contains("EventListeners.createObligation") || line.contains("EventListeners.activateObligation") || line.contains("EventListeners.createPower" ) || line.contains("EventListeners.activatePower") || line.contains('EventListeners.fulfillPower'))) { related=true } } if (survive && !related){ return " || contract.isSuccessfulTermination() || contract.isUnsuccessfulTermination()" } else return "" } }