Giter Site home page Giter Site logo

moddle's Introduction

moddle

CI

A utility library for working with meta-model based data structures.

What is it good for?

moddle offers you a concise way to define meta models in JavaScript. You can use these models to consume documents, create model elements, and perform model validation.

Define a schema

You start by creating a moddle schema. It is a JSON file which describes types, their properties, and relationships:

{
  "name": "Cars",
  "uri": "http://cars",
  "prefix": "c",
  "types": [
    {
      "name": "Base",
      "properties": [
        { "name": "id", "type": "String", "isAttr": true }
      ]
    },
    {
      "name": "Root",
      "superClass": [ "Base" ],
      "properties": [
        { "name": "cars", "type": "Car", "isMany": true }
      ]
    },
    {
      "name": "Car",
      "superClass": [ "Base" ],
      "properties": [
        { "name": "name", "type": "String", "isAttr": true, "default": "No Name" },
        { "name": "power", "type": "Integer", "isAttr": true },
        { "name": "similar", "type": "Car", "isMany": true, "isReference": true },
        { "name": "trunk", "type": "Element", "isMany": true }
      ]
    }
  ]
}

Instantiate moddle

You can instantiate a moddle instance with a set of defined schemas:

import { Moddle } from 'moddle';

var cars = new Moddle([ carsJSON ]);

Create objects

Use a moddle instance to create objects of your defined types:

var taiga = cars.create('c:Car', { name: 'Taiga' });

console.log(taiga);
// { $type: 'c:Car', name: 'Taiga' };


var cheapCar = cars.create('c:Car');

console.log(cheapCar.name);
// "No Name"


// really?
cheapCar.get('similar').push(taiga);

Introspect things

Then again, given the knowledge moddle has, you can perform deep introspection:

var carDescriptor = cheapCar.$descriptor;

console.log(carDescriptor.properties);
// [ { name: 'id', type: 'String', ... }, { name: 'name', type: 'String', ...} ... ]

Access extensions

moddle is friendly towards extensions and keeps unknown any properties around:

taiga.set('specialProperty', 'not known to moddle');

console.log(taiga.get('specialProperty'));
// 'not known to moddle'

It also allows you to create any elements for namespaces that you did not explicitly define:

var screwdriver = cars.createAny('tools:Screwdriver', 'http://tools', {
  make: 'ScrewIt!'
});

car.trunk.push(screwdriver);

There is more

Have a look at our test coverage to learn about everything that is currently supported.

Resources

Related

  • moddle-xml: read xml documents based on moddle descriptors

License

MIT

moddle's People

Contributors

barmac avatar felixlinker avatar fra-pa avatar iso50 avatar marstamm avatar maxtru avatar nikku avatar ohkai avatar pedesen avatar ricardomatias avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

moddle's Issues

Correctly handle element aliasing

The aliasing of an element in XML should be configurable via a package property.

Some packages (i.e. BPMN) apply a toLowerCase transformation on class names to retrieve the xml name.
Others (DI, BPMNDI, DC) keep the original name of classes in the xml file.

Suggestion to remove Element from BUILTIN types

Hi,

thanks for the moddle library, I try to use it in a project. Unfortunately, some
of the tests fail because moddle defines a built-in type ‘Element’ and the model
I want to implement (UML) uses an equaly named type as the root of its
inheritance hierarchy. This results in an incomplete descriptor, where
‘uml:Element’ is missing.

Besides from my use case, I don’t understand why Element is a built-in type. In
contrast to the other four types (String, Boolean, Integer and Real) Element
does not have any semantics or meaning and is not what I would call a basic data
type. It should better be defined in the context of a specific domain (imho).

I can see a value in the definiton of some fundamental data types. But the
implementation lacks a namespace to allow proper seperation.

Even though the removal might raise issues with existing implementations, the
resulting compatibility problems can be fixed by simply adding Element to the
model. On the other hand, I cannot find an easy workaround if a type with this
name is part of the model to be implemented, like in my use case.

Maybe I miss something and there is already a way to avoid such type conflicts?

Regards,
Frank

Add isId meta-data

  • allow attributes to be flagged with isId
  • remove generateId configuration (people have to define the id property manually as part of the meta-model

Is it possible to generate models from XSD?

Hello! This is more of a question.
Is it possible to generate the models from already existing XSD schemas?

I am trying to proxy/abstract an XML API with a JSON API in front of it. So far I haven't been able to find any module(s) that help do that, yours (with moddle-xml) is the closest to what I am looking for.

This is a simple explainer of what I am trying to achieve:

  1. Requester sends a JSON to my API
  2. Convert said JSON into XML
  3. Proxy the created XML to the provider's XML API
  4. Receive the XML response from provider
  5. Parse the XML to JSON
  6. Respond to the original requester in 1 using the parsed JSON

Cheers,
Pupix

Maintain `$parent` safely within the core

Is your feature request related to a problem? Please describe

The child to parent pointer $parent is important for model traversal. We do have basic test coverage for it, however we do not ensure that it is being maintained as part of the core. Instead, libraries using it (i.e. moddle-xml, bpmn-js) manually maintain the property in various places. This is error prone and not properly done everywhere.

At best, we do automatically maintain the property:

// $parent set during modeling
const foo = moddle.create('bpmn:ExtensionElements');

// during creation
const blub = { };
foo.values.push(blub); // blub = { $parent: foo }
foo.bar = { }; // bar = { $parent: foo }

// during removal
foo.values.pop(); // blub = { }
blub.$parent = null;
foo.bar = null; // bar = { }

Describe the solution you'd like

  • $parent is maintained automatically by moddle so that I can rely on it

Describe alternatives you've considered

Keep $parent maintenance out of scope for the library.

Additional context

Brought up in the context of bpmn-io/bpmnlint#69 (comment).

Exception in DescriptorBuilder for extension with redefines

Trying to get type information or create an instance of a type (c:CustomRoot) which extends another type (b:Root), inherits from a base type (c:Base) and redefines a property (b:Root#generic), the following exception is thrown:

Error: refined property <b:generic> not found
      at DescriptorBuilder._545‍.r.DescriptorBuilder.redefineProperty (lib/descriptor-builder.js:125:11)
      at DescriptorBuilder.<anonymous> (lib/descriptor-builder.js:219:12)
      at forEach (node_modules/min-dash/dist/index.js:170:20)
      at DescriptorBuilder._545‍.r.DescriptorBuilder.addTrait (lib/descriptor-builder.js:202:3)
      at lib/registry.js:185:13
      at Registry._a72‍.r.Registry.mapTypes (lib/registry.js:165:3)
      at Registry._a72‍.r.Registry.getEffectiveDescriptor (lib/registry.js:184:8)
      at Moddle._a72‍.r.Moddle.getType (lib/moddle.js:97:32)
      at Context.<anonymous> (test/spec/extension.js:58:28)
      at processImmediate (internal/timers.js:461:21)

DescriptorBuilder fails to lookup property b:Root#generic in propertiesByName, because b:Root is not a superClass of c:Base and therefore not found in the inheritance chain of c:CustomRoot.

The model (base.json and custom.json) used for the test looks something like this:

redefined-properties-ext-type

At runtime, the applied extension of b:Root by c:CustomRoot transforms the model (interpreted from the type descriptor Type.$descriptor; see below snippet from the spec) into:

redefined-properties-ext-type-b-root

Redefining properties of extended types is not explicitly documented, but specified in "moddle/extension/trait/should refine property":

it('should refine property', function() {
  // given
  var Type = model.getType('b:Root');

  // when
  var genericProperty = Type.$descriptor.propertiesByName['generic'];

  // then
  expect(genericProperty.type).to.eql('c:CustomGeneric');
});

The documentation describes the redefinition of properties:

redefines the property inherited from a super type, overriding name, type and qualifiers

This refers only to inheritance, not extension. c:CustomRoot uses both and it's explicitly tested, that the property b:Root#generic from the extendet type b:Root is redefined.

Maybe I miss something, but it should be possible, to getType() or create an instance of c:CustomRoot, right?

Regards,
Frank

Export to xml

Given the meta-model and a document tree we should be able to serialize a document tree to valid xml.

Error: no namespace uri given for prefix <undefined>

Hi,

I've been playing around with moddle for quite a while now but have hit issues of which I'm not too sure on how to proceed with.

Below is the roundtrip code. the errors and the test.json model. Would be really grateful if you could have a quick look to see if you could point out mistakes I've made. The harness I'm using is the same as bpmn-moddle.

roundtrip.js

'use strict';

var Helper = require('../../helper'),
    XMLHelper = require('../../xml-helper');

var toXML = XMLHelper.toXML,
    ids = new (require('diagram-js/lib/util/IdGenerator'))('idg');


describe('test-moddle - roundtrip', function() {

  var moddle = Helper.createModdle();


  describe('should serialize valid test xml after read', function() {

    this.timeout(15000);

    it('home-made test model', function(done) {

      var edgeHRef = ids.next();
      var id1 = ids.next();

      var outgoingActivityEdge = moddle.create('test:ActivityEdge', {
          href: edgeHRef
      });

      var resultOutputPin = moddle.create('test:OutputPin', {
          xmiId: id1,
          name: "Value.result",
          outgoing: outgoingActivityEdge,
//          type: typeActivityEdge
      });

      var valueOpaqueBehavior = moddle.create('test:OpaqueBehavior', {
          xmiId: ids.next(),
          body: "&quot;Bonjour Monde!&quot",
          language: "English"
      });

      var valueSpecificationAction = moddle.create('test:ValueSpecificationAction', {
          name: "Value(&quot;Bonjour Monde!&quot;)",
          xmiId: ids.next(),
          visibility: "public",
          value: valueOpaqueBehavior,
          result: resultOutputPin
      });

      var activityModel = moddle.create('test:Activity', {
          name: "HelloWorld",
          xmiId: ids.next(),
          visibility: "public",
          isReentrant: "false",
          node: [valueSpecificationAction]
      });
        
      toXML(activityModel, { format: true }, function(err, xml) {
        // then
//        validate(err, xml, done);
console.log(activityModel);
console.log(xml);
          done(err);
      });
    });

  });

});

error output

"/home/nick/node/node/bin/grunt"
Running "jshint:src" (jshint) task
>> 2 files lint free.

Running "jshint:gruntfile" (jshint) task
>> 1 file lint free.

Running "mochaTest:test" (mochaTest) task


  test-moddle - roundtrip
    should serialize valid test xml after read
missing namespace information for  xmiId = idg-127789124-4 on { '$type': 'test:ValueSpecificationAction',
  name: 'Value(&quot;Bonjour Monde!&quot;)',
  visibility: 'public',
  value: 
   { '$type': 'test:OpaqueBehavior',
     body: '&quot;Bonjour Monde!&quot',
     language: 'English' },
  result: 
   { '$type': 'test:OutputPin',
     name: 'Value.result',
     outgoing: { '$type': 'test:ActivityEdge' } } } [Error: no namespace uri given for prefix <undefined>]
missing namespace information for  xmiId = idg-127789124-5 on { '$type': 'test:Activity',
  name: 'HelloWorld',
  visibility: 'public',
  isReentrant: 'false',
  node: 
   [ { '$type': 'test:ValueSpecificationAction',
       name: 'Value(&quot;Bonjour Monde!&quot;)',
       visibility: 'public',
       value: [Object],
       result: [Object] } ] } [Error: no namespace uri given for prefix <undefined>]
{ '$type': 'test:Activity',
  name: 'HelloWorld',
  visibility: 'public',
  isReentrant: 'false',
  node: 
   [ { '$type': 'test:ValueSpecificationAction',
       name: 'Value(&quot;Bonjour Monde!&quot;)',
       visibility: 'public',
       value: [Object],
       result: [Object] } ] }
<?xml version="1.0" encoding="UTF-8"?>
<test:Activity xmlns:test="http://www.omg.org/spec/TEST" name="HelloWorld" visibility="public" isReentrant="false">
  <test:ValueSpecificationAction name="Value(&#38;quot;Bonjour Monde!&#38;quot;)" visibility="public" value="[object Object]" result="[object Object]" />
</test:Activity>

      ✓ home-made test model


  1 passing (9ms)


Running "mochaTest:watch" (mochaTest) task


  0 passing (0ms)


Done, without errors.
Done.

test.json

{
  "name": "TEST",
  "uri": "http://www.omg.org/spec/TEST",
  "associations": [],
  "types": [
    {
      "name": "OpaqueBehavior",
      "superClass": [
        "Behavior"
      ],
      "properties": [
        {
          "name": "body",
          "type": "String",
          "isMany": true
        },
        {
          "name": "language",
          "type": "String",
          "isMany": true
        }
      ]
    },
    {
      "name": "FunctionBehavior",
      "superClass": [
        "OpaqueBehavior"
      ]
    },
    {
      "name": "BehavioredClassifier",
      "superClass": [
        "Classifier"
      ],
      "properties": [
        {
          "name": "ownedBehavior",
          "type": "Behavior",
          "isMany": true
        },
        {
          "name": "classifierBehavior",
          "type": "Behavior",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Behavior",
      "superClass": [
        "Class"
      ],
      "properties": [
        {
          "name": "isReentrant",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "specification",
          "type": "BehavioralFeature",
          "isAttr": true
        },
        {
          "name": "ownedParameter",
          "type": "Parameter",
          "isMany": true
        },
        {
          "name": "context",
          "type": "BehavioredClassifier",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Trigger",
      "superClass": [
        "NamedElement"
      ],
      "properties": [
        {
          "name": "event",
          "type": "Event",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Signal",
      "superClass": [
        "Classifier"
      ],
      "properties": [
        {
          "name": "ownedAttribute",
          "type": "Property",
          "isMany": true
        }
      ]
    },
    {
      "name": "SignalEvent",
      "superClass": [
        "MessageEvent"
      ],
      "properties": [
        {
          "name": "signal",
          "type": "Signal",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Reception",
      "superClass": [
        "BehavioralFeature"
      ],
      "properties": [
        {
          "name": "signal",
          "type": "Signal",
          "isAttr": true
        }
      ]
    },
    {
      "name": "MessageEvent",
      "superClass": [
        "Event"
      ]
    },
    {
      "name": "Event",
      "superClass": [
        "PackageableElement"
      ]
    },
    {
      "name": "ValueSpecification",
      "superClass": [
        "TypedElement"
      ]
    },
    {
      "name": "StructuralFeature",
      "superClass": [
        "Feature",
        "TypedElement",
        "MultiplicityElement"
      ],
      "properties": [
        {
          "name": "isReadOnly",
          "type": "Boolean",
          "isAttr": true
        }
      ]
    },
    {
      "name": "RedefinableElement",
      "superClass": [
        "NamedElement"
      ],
      "properties": [
        {
          "name": "isLeaf",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "redefinedElement",
          "type": "RedefinableElement",
          "isMany": true
        },
        {
          "name": "redefinitionContext",
          "type": "Classifier",
          "isMany": true
        }
      ]
    },
    {
      "name": "BehavioralFeature",
      "superClass": [
        "Feature"
      ],
      "properties": [
        {
          "name": "ownedParameter",
          "type": "Parameter",
          "isMany": true
        },
        {
          "name": "isAbstract",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "method",
          "type": "Behavior",
          "isMany": true
        },
        {
          "name": "concurrency",
          "type": "CallConcurrencyKind",
          "isAttr": true
        }
      ]
    },
    {
      "name": "InstanceSpecification",
      "superClass": [
        "NamedElement"
      ],
      "properties": [
        {
          "name": "classifier",
          "type": "Classifier",
          "isMany": true
        },
        {
          "name": "slot",
          "type": "Slot",
          "isMany": true
        }
      ]
    },
    {
      "name": "InstanceValue",
      "superClass": [
        "ValueSpecification"
      ],
      "properties": [
        {
          "name": "instance",
          "type": "InstanceSpecification",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LiteralBoolean",
      "superClass": [
        "LiteralSpecification"
      ],
      "properties": [
        {
          "name": "value",
          "type": "Boolean",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LiteralInteger",
      "superClass": [
        "LiteralSpecification"
      ],
      "properties": [
        {
          "name": "value",
          "type": "Integer",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LiteralNull",
      "superClass": [
        "LiteralSpecification"
      ]
    },
    {
      "name": "LiteralSpecification",
      "superClass": [
        "ValueSpecification"
      ]
    },
    {
      "name": "LiteralString",
      "superClass": [
        "LiteralSpecification"
      ],
      "properties": [
        {
          "name": "value",
          "type": "String",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LiteralUnlimitedNatural",
      "superClass": [
        "LiteralSpecification"
      ],
      "properties": [
        {
          "name": "value",
          "type": "UnlimitedNatural",
          "isAttr": true
        }
      ]
    },
    {
      "name": "MultiplicityElement",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "isOrdered",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "isUnique",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "upper",
          "type": "UnlimitedNatural",
          "isAttr": true
        },
        {
          "name": "lower",
          "type": "Integer",
          "isAttr": true
        },
        {
          "name": "upperValue",
          "type": "ValueSpecification",
          "isAttr": true
        },
        {
          "name": "lowerValue",
          "type": "ValueSpecification",
          "isAttr": true
        }
      ]
    },
    {
      "name": "NamedElement",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "name",
          "type": "String",
          "isAttr": true
        },
        {
          "name": "visibility",
          "type": "VisibilityKind",
          "isAttr": true
        },
        {
          "name": "qualifiedName",
          "type": "String",
          "isAttr": true
        },
        {
          "name": "namespace",
          "type": "Namespace",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Namespace",
      "superClass": [
        "NamedElement"
      ],
      "properties": [
        {
          "name": "member",
          "type": "NamedElement",
          "isMany": true
        },
        {
          "name": "ownedMember",
          "type": "NamedElement",
          "isMany": true
        },
        {
          "name": "elementImport",
          "type": "ElementImport",
          "isMany": true
        },
        {
          "name": "packageImport",
          "type": "PackageImport",
          "isMany": true
        },
        {
          "name": "importedMember",
          "type": "PackageableElement",
          "isMany": true
        }
      ]
    },
    {
      "name": "Operation",
      "superClass": [
        "BehavioralFeature"
      ],
      "properties": [
        {
          "name": "isQuery",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "isOrdered",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "isUnique",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "lower",
          "type": "Integer",
          "isAttr": true
        },
        {
          "name": "upper",
          "type": "UnlimitedNatural",
          "isAttr": true
        },
        {
          "name": "class",
          "type": "Class",
          "isAttr": true
        },
        {
          "name": "redefinedOperation",
          "type": "Operation",
          "isMany": true
        },
        {
          "name": "type",
          "type": "Type",
          "isAttr": true
        },
        {
          "name": "ownedParameter",
          "type": "Parameter",
          "isMany": true,
          "redefines": "test:BehavioralFeature#ownedParameter"
        }
      ]
    },
    {
      "name": "Package",
      "superClass": [
        "Namespace",
        "PackageableElement"
      ],
      "properties": [
        {
          "name": "packagedElement",
          "type": "PackageableElement",
          "isMany": true
        },
        {
          "name": "ownedType",
          "type": "Type",
          "isMany": true
        },
        {
          "name": "nestedPackage",
          "type": "Package",
          "isMany": true
        },
        {
          "name": "nestingPackage",
          "type": "Package",
          "isAttr": true
        },
        {
          "name": "URI",
          "type": "String",
          "isAttr": true
        }
      ]
    },
    {
      "name": "PackageableElement",
      "superClass": [
        "NamedElement"
      ],
      "properties": [
        {
          "name": "visibility",
          "type": "VisibilityKind",
          "isAttr": true,
          "redefines": "test:NamedElement#visibility"
        }
      ]
    },
    {
      "name": "Parameter",
      "superClass": [
        "TypedElement",
        "MultiplicityElement"
      ],
      "properties": [
        {
          "name": "direction",
          "type": "ParameterDirectionKind",
          "isAttr": true
        },
        {
          "name": "operation",
          "type": "Operation",
          "isAttr": true
        }
      ]
    },
    {
      "name": "PrimitiveType",
      "superClass": [
        "DataType"
      ]
    },
    {
      "name": "Property",
      "superClass": [
        "StructuralFeature"
      ],
      "properties": [
        {
          "name": "isDerived",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "isReadOnly",
          "type": "Boolean",
          "isAttr": true,
          "redefines": "test:StructuralFeature#isReadOnly"
        },
        {
          "name": "isDerivedUnion",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "aggregation",
          "type": "AggregationKind",
          "isAttr": true
        },
        {
          "name": "isComposite",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "owningAssociation",
          "type": "Association",
          "isAttr": true
        },
        {
          "name": "datatype",
          "type": "DataType",
          "isAttr": true
        },
        {
          "name": "association",
          "type": "Association",
          "isAttr": true
        },
        {
          "name": "class",
          "type": "Class",
          "isAttr": true
        },
        {
          "name": "opposite",
          "type": "Property",
          "isAttr": true
        },
        {
          "name": "isID",
          "type": "Boolean",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Slot",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "owningInstance",
          "type": "InstanceSpecification",
          "isAttr": true
        },
        {
          "name": "definingFeature",
          "type": "StructuralFeature",
          "isAttr": true
        },
        {
          "name": "value",
          "type": "ValueSpecification",
          "isMany": true
        }
      ]
    },
    {
      "name": "Type",
      "superClass": [
        "PackageableElement"
      ],
      "properties": [
        {
          "name": "package",
          "type": "Package",
          "isAttr": true
        }
      ]
    },
    {
      "name": "TypedElement",
      "superClass": [
        "NamedElement"
      ],
      "properties": [
        {
          "name": "type",
          "type": "Type",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Class",
      "superClass": [
        "BehavioredClassifier"
      ],
      "properties": [
        {
          "name": "ownedOperation",
          "type": "Operation",
          "isMany": true
        },
        {
          "name": "isActive",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "ownedReception",
          "type": "Reception",
          "isMany": true
        },
        {
          "name": "ownedAttribute",
          "type": "Property",
          "isMany": true
        },
        {
          "name": "nestedClassifier",
          "type": "Classifier",
          "isMany": true
        },
        {
          "name": "isID",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "superClass",
          "type": "Class",
          "isMany": true
        }
      ]
    },
    {
      "name": "Classifier",
      "superClass": [
        "Type",
        "Namespace"
      ],
      "properties": [
        {
          "name": "isAbstract",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "generalization",
          "type": "Generalization",
          "isMany": true
        },
        {
          "name": "feature",
          "type": "Feature",
          "isMany": true
        },
        {
          "name": "inheritedMember",
          "type": "NamedElement",
          "isMany": true
        },
        {
          "name": "attribute",
          "type": "Property",
          "isMany": true
        },
        {
          "name": "general",
          "type": "Classifier",
          "isMany": true
        },
        {
          "name": "isFinalSpecialization",
          "type": "Boolean",
          "isAttr": true
        }
      ]
    },
    {
      "name": "DataType",
      "superClass": [
        "Classifier"
      ],
      "properties": [
        {
          "name": "ownedAttribute",
          "type": "Property",
          "isMany": true
        }
      ]
    },
    {
      "name": "Element",
      "properties": [
        {
          "name": "ownedElement",
          "type": "Element",
          "isMany": true
        },
        {
          "name": "owner",
          "type": "Element",
          "isAttr": true
        },
        {
          "name": "ownedComment",
          "type": "Comment",
          "isMany": true
        },
        {
          "name": "xmiId",
          "type": "String",
          "isAttr": true,
          "isId": true
        },
        {
          "name": "href",
          "type": "String",
          "isAttr": true
        },
        {
          "name": "xmiNamespace",
          "type": "String",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Enumeration",
      "superClass": [
        "DataType"
      ],
      "properties": [
        {
          "name": "ownedLiteral",
          "type": "EnumerationLiteral",
          "isMany": true
        }
      ]
    },
    {
      "name": "EnumerationLiteral",
      "superClass": [
        "InstanceSpecification"
      ],
      "properties": [
        {
          "name": "enumeration",
          "type": "Enumeration",
          "isAttr": true
        },
        {
          "name": "classifier",
          "type": "Enumeration",
          "isAttr": true,
          "redefines": "test:InstanceSpecification#classifier"
        }
      ]
    },
    {
      "name": "Feature",
      "superClass": [
        "RedefinableElement"
      ],
      "properties": [
        {
          "name": "isStatic",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "featuringClassifier",
          "type": "Classifier",
          "isMany": true
        }
      ]
    },
    {
      "name": "Generalization",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "isSubstitutable",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "specific",
          "type": "Classifier",
          "isAttr": true
        },
        {
          "name": "general",
          "type": "Classifier",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Association",
      "superClass": [
        "Classifier"
      ],
      "properties": [
        {
          "name": "isDerived",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "ownedEnd",
          "type": "Property",
          "isMany": true
        },
        {
          "name": "endType",
          "type": "Type",
          "isMany": true
        },
        {
          "name": "memberEnd",
          "type": "Property",
          "isMany": true
        },
        {
          "name": "navigableOwnedEnd",
          "type": "Property",
          "isMany": true
        }
      ]
    },
    {
      "name": "Comment",
      "properties": [
        {
          "name": "annotatedElement",
          "type": "Element",
          "isMany": true
        },
        {
          "name": "body",
          "type": "String",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ElementImport",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "visibility",
          "type": "VisibilityKind",
          "isAttr": true
        },
        {
          "name": "alias",
          "type": "String",
          "isAttr": true
        },
        {
          "name": "importedElement",
          "type": "PackageableElement",
          "isAttr": true
        },
        {
          "name": "importingNamespace",
          "type": "Namespace",
          "isAttr": true
        }
      ]
    },
    {
      "name": "PackageImport",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "visibility",
          "type": "VisibilityKind",
          "isAttr": true
        },
        {
          "name": "importingNamespace",
          "type": "Namespace",
          "isAttr": true
        },
        {
          "name": "importedPackage",
          "type": "Package",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LiteralReal",
      "superClass": [
        "LiteralSpecification"
      ],
      "properties": [
        {
          "name": "value",
          "type": "Real",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ObjectFlow",
      "superClass": [
        "ActivityEdge"
      ]
    },
    {
      "name": "ObjectNode",
      "superClass": [
        "ActivityNode",
        "TypedElement"
      ]
    },
    {
      "name": "MergeNode",
      "superClass": [
        "ControlNode"
      ]
    },
    {
      "name": "JoinNode",
      "superClass": [
        "ControlNode"
      ]
    },
    {
      "name": "InitialNode",
      "superClass": [
        "ControlNode"
      ]
    },
    {
      "name": "FinalNode",
      "superClass": [
        "ControlNode"
      ]
    },
    {
      "name": "ForkNode",
      "superClass": [
        "ControlNode"
      ]
    },
    {
      "name": "ControlFlow",
      "superClass": [
        "ActivityEdge"
      ]
    },
    {
      "name": "ControlNode",
      "superClass": [
        "ActivityNode"
      ]
    },
    {
      "name": "DecisionNode",
      "superClass": [
        "ControlNode"
      ],
      "properties": [
        {
          "name": "decisionInput",
          "type": "Behavior",
          "isAttr": true
        },
        {
          "name": "decisionInputFlow",
          "type": "ObjectFlow",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ActivityFinalNode",
      "superClass": [
        "FinalNode"
      ]
    },
    {
      "name": "ActivityNode",
      "superClass": [
        "RedefinableElement"
      ],
      "properties": [
        {
          "name": "inStructuredNode",
          "type": "StructuredActivityNode",
          "isAttr": true
        },
        {
          "name": "activity",
          "type": "Activity",
          "isAttr": true
        },
        {
          "name": "outgoing",
          "type": "ActivityEdge",
          "isMany": true
        },
        {
          "name": "incoming",
          "type": "ActivityEdge",
          "isMany": true
        }
      ]
    },
    {
      "name": "ActivityParameterNode",
      "superClass": [
        "ObjectNode"
      ],
      "properties": [
        {
          "name": "parameter",
          "type": "Parameter",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ActivityEdge",
      "superClass": [
        "RedefinableElement"
      ],
      "properties": [
        {
          "name": "activity",
          "type": "Activity",
          "isAttr": true
        },
        {
          "name": "source",
          "type": "ActivityNode",
          "isAttr": true
        },
        {
          "name": "target",
          "type": "ActivityNode",
          "isAttr": true
        },
        {
          "name": "guard",
          "type": "ValueSpecification",
          "isAttr": true
        },
        {
          "name": "inStructuredNode",
          "type": "StructuredActivityNode",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Activity",
      "superClass": [
        "Behavior"
      ],
      "properties": [
        {
          "name": "structuredNode",
          "type": "StructuredActivityNode",
          "isMany": true
        },
        {
          "name": "node",
          "type": "ActivityNode",
          "isMany": true
        },
        {
          "name": "isReadOnly",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "edge",
          "type": "ActivityEdge",
          "isMany": true
        }
      ]
    },
    {
      "name": "FlowFinalNode",
      "superClass": [
        "FinalNode"
      ]
    },
    {
      "name": "StructuredActivityNode",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "node",
          "type": "ActivityNode",
          "isMany": true
        },
        {
          "name": "activity",
          "type": "Activity",
          "isAttr": true,
          "redefines": "test:ActivityNode#activity"
        },
        {
          "name": "mustIsolate",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "edge",
          "type": "ActivityEdge",
          "isMany": true
        },
        {
          "name": "structuredNodeOutput",
          "type": "OutputPin",
          "isMany": true
        },
        {
          "name": "structuredNodeInput",
          "type": "InputPin",
          "isMany": true
        }
      ]
    },
    {
      "name": "LoopNode",
      "superClass": [
        "StructuredActivityNode"
      ],
      "properties": [
        {
          "name": "isTestedFirst",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "decider",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "test",
          "type": "ExecutableNode",
          "isMany": true
        },
        {
          "name": "bodyOutput",
          "type": "OutputPin",
          "isMany": true
        },
        {
          "name": "loopVariableInput",
          "type": "InputPin",
          "isMany": true
        },
        {
          "name": "bodyPart",
          "type": "ExecutableNode",
          "isMany": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isMany": true
        },
        {
          "name": "loopVariable",
          "type": "OutputPin",
          "isMany": true
        },
        {
          "name": "setupPart",
          "type": "ExecutableNode",
          "isMany": true
        }
      ]
    },
    {
      "name": "ExecutableNode",
      "superClass": [
        "ActivityNode"
      ]
    },
    {
      "name": "Clause",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "test",
          "type": "ExecutableNode",
          "isMany": true
        },
        {
          "name": "body",
          "type": "ExecutableNode",
          "isMany": true
        },
        {
          "name": "predecessorClause",
          "type": "Clause",
          "isMany": true
        },
        {
          "name": "successorClause",
          "type": "Clause",
          "isMany": true
        },
        {
          "name": "decider",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "bodyOutput",
          "type": "OutputPin",
          "isMany": true
        }
      ]
    },
    {
      "name": "ConditionalNode",
      "superClass": [
        "StructuredActivityNode"
      ],
      "properties": [
        {
          "name": "isDeterminate",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "isAssured",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "clause",
          "type": "Clause",
          "isMany": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isMany": true
        }
      ]
    },
    {
      "name": "ExpansionNode",
      "superClass": [
        "ObjectNode"
      ],
      "properties": [
        {
          "name": "regionAsOutput",
          "type": "ExpansionRegion",
          "isAttr": true
        },
        {
          "name": "regionAsInput",
          "type": "ExpansionRegion",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ExpansionRegion",
      "superClass": [
        "StructuredActivityNode"
      ],
      "properties": [
        {
          "name": "mode",
          "type": "ExpansionKind",
          "isAttr": true
        },
        {
          "name": "outputElement",
          "type": "ExpansionNode",
          "isMany": true
        },
        {
          "name": "inputElement",
          "type": "ExpansionNode",
          "isMany": true
        }
      ]
    },
    {
      "name": "SendSignalAction",
      "superClass": [
        "InvocationAction"
      ],
      "properties": [
        {
          "name": "target",
          "type": "InputPin",
          "isAttr": true
        },
        {
          "name": "signal",
          "type": "Signal",
          "isAttr": true
        }
      ]
    },
    {
      "name": "OutputPin",
      "superClass": [
        "Pin"
      ]
    },
    {
      "name": "Pin",
      "superClass": [
        "ObjectNode",
        "MultiplicityElement"
      ]
    },
    {
      "name": "InputPin",
      "superClass": [
        "Pin"
      ]
    },
    {
      "name": "InvocationAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "argument",
          "type": "InputPin",
          "isMany": true
        }
      ]
    },
    {
      "name": "CallAction",
      "superClass": [
        "InvocationAction"
      ],
      "properties": [
        {
          "name": "isSynchronous",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isMany": true
        }
      ]
    },
    {
      "name": "CallBehaviorAction",
      "superClass": [
        "CallAction"
      ],
      "properties": [
        {
          "name": "behavior",
          "type": "Behavior",
          "isAttr": true
        }
      ]
    },
    {
      "name": "CallOperationAction",
      "superClass": [
        "CallAction"
      ],
      "properties": [
        {
          "name": "operation",
          "type": "Operation",
          "isAttr": true
        },
        {
          "name": "target",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "Action",
      "superClass": [
        "ExecutableNode"
      ],
      "properties": [
        {
          "name": "output",
          "type": "OutputPin",
          "isMany": true
        },
        {
          "name": "context",
          "type": "Classifier",
          "isAttr": true
        },
        {
          "name": "input",
          "type": "InputPin",
          "isMany": true
        },
        {
          "name": "isLocallyReentrant",
          "type": "Boolean",
          "isAttr": true
        }
      ]
    },
    {
      "name": "StructuralFeatureAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "structuralFeature",
          "type": "StructuralFeature",
          "isAttr": true
        },
        {
          "name": "object",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "TestIdentityAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "second",
          "type": "InputPin",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "first",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ValueSpecificationAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "value",
          "type": "ValueSpecification",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "WriteLinkAction",
      "superClass": [
        "LinkAction"
      ]
    },
    {
      "name": "WriteStructuralFeatureAction",
      "superClass": [
        "StructuralFeatureAction"
      ],
      "properties": [
        {
          "name": "value",
          "type": "InputPin",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "RemoveStructuralFeatureValueAction",
      "superClass": [
        "WriteStructuralFeatureAction"
      ],
      "properties": [
        {
          "name": "isRemoveDuplicates",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "removeAt",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReadLinkAction",
      "superClass": [
        "LinkAction"
      ],
      "properties": [
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReadSelfAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReadStructuralFeatureAction",
      "superClass": [
        "StructuralFeatureAction"
      ],
      "properties": [
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LinkAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "endData",
          "type": "LinkEndData",
          "isMany": true
        },
        {
          "name": "inputValue",
          "type": "InputPin",
          "isMany": true
        }
      ]
    },
    {
      "name": "LinkEndCreationData",
      "superClass": [
        "LinkEndData"
      ],
      "properties": [
        {
          "name": "isReplaceAll",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "insertAt",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LinkEndData",
      "superClass": [
        "Element"
      ],
      "properties": [
        {
          "name": "value",
          "type": "InputPin",
          "isAttr": true
        },
        {
          "name": "end",
          "type": "Property",
          "isAttr": true
        }
      ]
    },
    {
      "name": "LinkEndDestructionData",
      "superClass": [
        "LinkEndData"
      ],
      "properties": [
        {
          "name": "isDestroyDuplicates",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "destroyAt",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ClearAssociationAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "association",
          "type": "Association",
          "isAttr": true
        },
        {
          "name": "object",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ClearStructuralFeatureAction",
      "superClass": [
        "StructuralFeatureAction"
      ],
      "properties": [
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "CreateLinkAction",
      "superClass": [
        "WriteLinkAction"
      ],
      "properties": [
        {
          "name": "endData",
          "type": "LinkEndCreationData",
          "isMany": true,
          "redefines": "test:LinkAction#endData"
        }
      ]
    },
    {
      "name": "CreateObjectAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "classifier",
          "type": "Classifier",
          "isAttr": true
        }
      ]
    },
    {
      "name": "DestroyLinkAction",
      "superClass": [
        "WriteLinkAction"
      ],
      "properties": [
        {
          "name": "endData",
          "type": "LinkEndDestructionData",
          "isMany": true,
          "redefines": "test:LinkAction#endData"
        }
      ]
    },
    {
      "name": "DestroyObjectAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "isDestroyLinks",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "isDestroyOwnedObjects",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "target",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "AddStructuralFeatureValueAction",
      "superClass": [
        "WriteStructuralFeatureAction"
      ],
      "properties": [
        {
          "name": "isReplaceAll",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "insertAt",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "StartClassifierBehaviorAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "object",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "StartObjectBehaviorAction",
      "superClass": [
        "CallAction"
      ],
      "properties": [
        {
          "name": "object",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReduceAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "reducer",
          "type": "Behavior",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "collection",
          "type": "InputPin",
          "isAttr": true
        },
        {
          "name": "isOrdered",
          "type": "Boolean",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReadExtentAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "classifier",
          "type": "Classifier",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReadIsClassifiedObjectAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "isDirect",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "classifier",
          "type": "Classifier",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isAttr": true
        },
        {
          "name": "object",
          "type": "InputPin",
          "isAttr": true
        }
      ]
    },
    {
      "name": "ReclassifyObjectAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "isReplaceAll",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "oldClassifier",
          "type": "Classifier",
          "isMany": true
        },
        {
          "name": "object",
          "type": "InputPin",
          "isAttr": true
        },
        {
          "name": "newClassifier",
          "type": "Classifier",
          "isMany": true
        }
      ]
    },
    {
      "name": "AcceptEventAction",
      "superClass": [
        "Action"
      ],
      "properties": [
        {
          "name": "isUnmarshall",
          "type": "Boolean",
          "isAttr": true
        },
        {
          "name": "result",
          "type": "OutputPin",
          "isMany": true
        },
        {
          "name": "trigger",
          "type": "Trigger",
          "isMany": true
        }
      ]
    }
  ],
  "enumerations": [
    {
      "name": "CallConcurrencyKind",
      "literalValues": [
        {
          "name": "sequential"
        }
      ]
    },
    {
      "name": "AggregationKind",
      "literalValues": [
        {
          "name": "none"
        },
        {
          "name": "shared"
        },
        {
          "name": "composite"
        }
      ]
    },
    {
      "name": "ParameterDirectionKind",
      "literalValues": [
        {
          "name": "in"
        },
        {
          "name": "inout"
        },
        {
          "name": "out"
        },
        {
          "name": "return"
        }
      ]
    },
    {
      "name": "VisibilityKind",
      "literalValues": [
        {
          "name": "public"
        },
        {
          "name": "private"
        },
        {
          "name": "protected"
        },
        {
          "name": "package"
        }
      ]
    },
    {
      "name": "ExpansionKind",
      "literalValues": [
        {
          "name": "parallel"
        },
        {
          "name": "iterative"
        },
        {
          "name": "stream"
        }
      ]
    }
  ],
  "prefix": "test"
}

Allow property positioning to be changed

Add an additional modifier replaces that allows users to change property position in subclasses.

This is required to make certain meta-model adjustments possible (example: bpmn-moddle).

Support element bodies

We need to support body simple:

<documentation>
SOME DOC TEXT
</documentation>

And via nested tags:

<some-element>
  <text>SOME DOC TEXT</text>
</some-element>

Reject non-string and empty property name in `set()`

Describe the Bug

Currently, when I pass a non-string or empty property name to set(), it's accepted by moddle. Non-strings are converted, so NaN becomes "NaN" and lands in $attrs. This allows buggy behavior like the one in bpmn-io/bpmn-js-properties-panel#775

Steps to Reproduce

import { Moddle } from 'moddle';

var cars = new Moddle([ carsJSON ]);

var taiga = cars.create('c:Car', { name: 'Taiga' });

taiga.set(undefined, 'hello');
taiga.set('', 'hello');
taiga.set(NaN, 'hello');

// no errors thrown

Expected Behavior

Moddle should throw a TypeError.

Environment

  • Host (Browser/Node version), if applicable: [e.g. MS Edge 18, Chrome 69, Node 10 LTS]
  • OS: [e.g. Windows 7]
  • Library version: [e.g. 2.0.0]

Add prepare script to package.json

Hi,

the NodeJS package manager supports installation of a package from git repositories as an option to the registry. Because there are no distribution steps executed when using this method, only the files contained in the repository (without the exclusions in .npmignore) are installed to the node_modules directory. For moddle, the dist folder is missing and therefore requiring or importing modules fails and the package cannot be used.

If package.json contains a prepare script, it will be run on a local npm install and can be utilized to create necessary artifacts for the usage of the package (see npm docu).

As this can be quite useful for development and testing, I suggest to add the prepare script to package.json.

Regards,
Frank

Exception in DescriptorBuilder when inherited properties have the same name

Hi,

the DescriptorBuilder throws an exception if a property with the same name is defined in the inheritance chain of more than one of its super classes:

Error: property <any> already defined; override of <mh:AnotherRoot#mh:any> by <props:Root#props:any> not allowed without redefines
      at DescriptorBuilder._fd7‍.r.DescriptorBuilder.assertNotDefined (lib/descriptor-builder.js:180:11)
      at DescriptorBuilder._fd7‍.r.DescriptorBuilder.addNamedProperty (lib/descriptor-builder.js:138:10)
      at DescriptorBuilder._fd7‍.r.DescriptorBuilder.addProperty (lib/descriptor-builder.js:53:8)
      at DescriptorBuilder.<anonymous> (lib/descriptor-builder.js:227:12)
      at forEach (node_modules/min-dash/dist/index.js:170:20)
      at DescriptorBuilder._fd7‍.r.DescriptorBuilder.addTrait (lib/descriptor-builder.js:202:3)
      at lib/registry.js:185:13
      at Registry._0ac‍.r.Registry.mapTypes (lib/registry.js:165:3)
      at traverseSuper (lib/registry.js:155:10)
      at forEach (node_modules/min-dash/dist/index.js:170:20)
      at Registry._0ac‍.r.Registry.mapTypes (lib/registry.js:162:3)
      at Registry._0ac‍.r.Registry.getEffectiveDescriptor (lib/registry.js:184:8)
      at Moddle._1b8‍.r.Moddle.getType (lib/moddle.js:97:32)
      at Moddle._1b8‍.r.Moddle.create (lib/moddle.js:65:19)
      at Context.<anonymous> (test/spec/properties.js:589:34)
      at processImmediate (internal/timers.js:461:21)

I created a test case with a simplified hierarchy:
multiple-inherited-properties

mh:MultipleInherited has two properties named any:

  • mh:AnotherRoot#any
  • props:Root#any

and the DescriptorBuilder does not map them into seperate namespaces.

In my use-case, the problem occurs with the UMLDI meta-model, where all the diagram types inherit from UMLDI:UMLDiagram:
umldi-inheritance

Regards,
Frank

Make object default values available via its prototype

We should have a general solution that allows us to model default values.

This should fix issues addressed in #9 and allow a consistent access to default values from javascript and during serialization.

Example

var foo = moddle.create('foo');

// accessing default value via prototype
foo.someValueWithDefault; // 'BAR'
foo.hasOwnProperty('someValueWithDefault'); // false

// accessing actual value
foo.someValueWithDefault = 'FOOBAR';
foo.hasOwnProperty('someValueWithDefault'); // true

// removing actual value
delete foo.someValueWithDefault;

Notes

  • Some values such as collection properties must not be removable and cannot be provided via the prototype either

Keep original namespace prefix after roundtrip

At the moment we will always use a preconfigured default namespace when serializing a model to xml. We could add an option to keep the namespace that was found when importing the diagram.

This would lead to less touched lines if only minor changes have been done.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.