processmaker-builder
Version:
The gulp task runner for ProcessMaker building
2,019 lines (1,550 loc) • 194 kB
JavaScript
'use strict';
function Base() { }
Base.prototype.get = function(name) {
return this.$model.properties.get(this, name);
};
Base.prototype.set = function(name, value) {
this.$model.properties.set(this, name, value);
};
//
//module.exports = Base;
var parseName = function(name, defaultPrefix) {
var parts = name.split(/:/),
localName, prefix;
// no prefix (i.e. only local name)
if (parts.length === 1) {
localName = name;
prefix = defaultPrefix;
} else
// prefix + local name
if (parts.length === 2) {
localName = parts[1];
prefix = parts[0];
} else {
throw new Error('expected <prefix:localName> or <localName>, got ' + name);
}
name = (prefix ? prefix + ':' : '') + localName;
return {
name: name,
prefix: prefix,
localName: localName
};
};
'use strict';
/**
* A utility that gets and sets properties of model elements.
*
* @param {Model} model
*/
function Properties(model) {
this.model = model;
}
//module.exports = Properties;
/**
* Sets a named property on the target element
*
* @param {Object} target
* @param {String} name
* @param {Object} value
*/
Properties.prototype.set = function(target, name, value) {
var property = this.model.getPropertyDescriptor(target, name);
if (!property) {
target.$attrs[name] = value;
} else {
Object.defineProperty(target, property.name, {
enumerable: !property.isReference,
writable: true,
value: value
});
}
};
/**
* Returns the named property of the given element
*
* @param {Object} target
* @param {String} name
*
* @return {Object}
*/
Properties.prototype.get = function(target, name) {
var property = this.model.getPropertyDescriptor(target, name);
if (!property) {
return target.$attrs[name];
}
var propertyName = property.name;
// check if access to collection property and lazily initialize it
if (!target[propertyName] && property.isMany) {
Object.defineProperty(target, propertyName, {
enumerable: !property.isReference,
writable: true,
value: []
});
}
return target[propertyName];
};
/**
* Define a property on the target element
*
* @param {Object} target
* @param {String} name
* @param {Object} options
*/
Properties.prototype.define = function(target, name, options) {
Object.defineProperty(target, name, options);
};
/**
* Define the descriptor for an element
*/
Properties.prototype.defineDescriptor = function(target, descriptor) {
this.define(target, '$descriptor', { value: descriptor });
};
/**
* Define the model for an element
*/
Properties.prototype.defineModel = function(target, model) {
this.define(target, '$model', { value: model });
};
'use strict';
//var _ = require('lodash');
//
//var Base = require('./base');
function Factory(model, properties) {
this.model = model;
this.properties = properties;
}
//module.exports = Factory;
Factory.prototype.createType = function(descriptor) {
var model = this.model;
var props = this.properties,
prototype = Object.create(Base.prototype);
// initialize default values
_.forEach(descriptor.properties, function(p) {
if (!p.isMany && p.default !== undefined) {
prototype[p.name] = p.default;
}
});
props.defineModel(prototype, model);
props.defineDescriptor(prototype, descriptor);
var name = descriptor.ns.name;
/**
* The new type constructor
*/
function ModdleElement(attrs) {
props.define(this, '$type', { value: name, enumerable: true });
props.define(this, '$attrs', { value: {} });
props.define(this, '$parent', { writable: true });
_.forEach(attrs, function(val, key) {
this.set(key, val);
}, this);
}
ModdleElement.prototype = prototype;
ModdleElement.hasType = prototype.$instanceOf = this.model.hasType;
// static links
props.defineModel(ModdleElement, model);
props.defineDescriptor(ModdleElement, descriptor);
return ModdleElement;
};
/**
* Built-in moddle types
*/
var BUILTINS = {
String: true,
Boolean: true,
Integer: true,
Real: true,
Element: true
};
/**
* Converters for built in types from string representations
*/
var TYPE_CONVERTERS = {
String: function(s) { return s; },
Boolean: function(s) { return s === 'true'; },
Integer: function(s) { return parseInt(s, 10); },
Real: function(s) { return parseFloat(s, 10); }
};
/**
* Convert a type to its real representation
*/
var coerceType = function(type, value) {
var converter = TYPE_CONVERTERS[type];
if (converter) {
return converter(value);
} else {
return value;
}
};
/**
* Return whether the given type is built-in
*/
var isBuiltIn = function(type) {
return !!BUILTINS[type];
};
/**
* Return whether the given type is simple
*/
var isSimple = function(type) {
return !!TYPE_CONVERTERS[type];
};
'use strict';
//var _ = require('lodash');
//
//var Types = require('./types');
// DescriptorBuilder = require('./descriptor-builder');
//
//var this.parseName = parseName;
function Registry(packages, properties, options) {
this.options = _.extend({ generateId: 'id' }, options || {});
this.packageMap = {};
this.typeMap = {};
this.packages = [];
this.properties = properties;
_.forEach(packages, this.registerPackage, this);
}
//module.exports = Registry;
Registry.prototype.getPackage = function(uriOrPrefix) {
return this.packageMap[uriOrPrefix];
};
Registry.prototype.getPackages = function() {
return this.packages;
};
Registry.prototype.registerPackage = function(pkg) {
// register types
_.forEach(pkg.types, function(descriptor) {
this.registerType(descriptor, pkg);
}, this);
this.packageMap[pkg.uri] = this.packageMap[pkg.prefix] = pkg;
this.packages.push(pkg);
};
/**
* Register a type from a specific package with us
*/
Registry.prototype.registerType = function(type, pkg) {
var ns = this.parseName(type.name, pkg.prefix),
name = ns.name,
propertiesByName = {};
// parse properties
_.forEach(type.properties, function(p) {
// namespace property names
var propertyNs = this.parseName(p.name, ns.prefix),
propertyName = propertyNs.name;
// namespace property types
if (!isBuiltIn( p.type)) {
p.type = this.parseName(p.type, propertyNs.prefix).name;
}
_.extend(p, {
ns: propertyNs,
name: propertyName
});
propertiesByName[propertyName] = p;
},this);
// update ns + name
_.extend(type, {
ns: ns,
name: name,
propertiesByName: propertiesByName
});
// link to package
this.definePackage(type, pkg);
// register
this.typeMap[name] = type;
};
/**
* Traverse the type hierarchy from bottom to top.
*/
Registry.prototype.mapTypes = function(nsName, iterator) {
var type = this.typeMap[nsName.name];
if (!type) {
throw new Error('unknown type <' + nsName.name + '>');
}
_.forEach(type.superClass, function(cls) {
var parentNs = this.parseName(cls, nsName.prefix);
this.mapTypes(parentNs, iterator);
}, this);
iterator(type);
};
/**
* Returns the effective descriptor for a type.
*
* @param {String} type the namespaced name (ns:localName) of the type
*
* @return {Descriptor} the resulting effective descriptor
*/
Registry.prototype.getEffectiveDescriptor = function(name) {
var options = this.options,
nsName = this.parseName(name);
var builder = new DescriptorBuilder(nsName);
this.mapTypes(nsName, function(type) {
builder.addTrait(type);
});
// check we have an id assigned
var id = this.options.generateId;
if (id && !builder.hasProperty(id)) {
builder.addIdProperty(id);
}
var descriptor = builder.build();
// define package link
this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg);
return descriptor;
};
Registry.prototype.definePackage = function(target, pkg) {
this.properties.define(target, '$pkg', { value: pkg });
};
Registry.prototype.parseName = function(name, defaultPrefix) {
var parts = name.split(/:/),
localName, prefix;
// no prefix (i.e. only local name)
if (parts.length === 1) {
localName = name;
prefix = defaultPrefix;
} else
// prefix + local name
if (parts.length === 2) {
localName = parts[1];
prefix = parts[0];
} else {
throw new Error('expected <prefix:localName> or <localName>, got ' + name);
}
name = (prefix ? prefix + ':' : '') + localName;
return {
name: name,
prefix: prefix,
localName: localName
};
};
'use strict';
//var _ = require('lodash');
//
//var parseNameNs = require('./ns').parseName;
function DescriptorBuilder(nameNs) {
this.ns = nameNs;
this.name = nameNs.name;
this.allTypes = [];
this.properties = [];
this.propertiesByName = {};
}
//module.exports = DescriptorBuilder;
DescriptorBuilder.prototype.build = function() {
return _.pick(this, [ 'ns', 'name', 'allTypes', 'properties', 'propertiesByName', 'bodyProperty' ]);
};
DescriptorBuilder.prototype.addProperty = function(p, idx) {
this.addNamedProperty(p, true);
var properties = this.properties;
if (idx !== undefined) {
properties.splice(idx, 0, p);
} else {
properties.push(p);
}
};
DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty) {
var oldNameNs = oldProperty.ns;
var props = this.properties,
propertiesByName = this.propertiesByName;
if (oldProperty.isBody) {
if (!newProperty.isBody) {
throw new Error(
'property <' + newProperty.ns.name + '> must be body property ' +
'to refine <' + oldProperty.ns.name + '>');
}
// TODO: Check compatibility
this.setBodyProperty(newProperty, false);
}
this.addNamedProperty(newProperty, true);
// replace old property at index with new one
var idx = props.indexOf(oldProperty);
if (idx === -1) {
throw new Error('property <' + oldNameNs.name + '> not found in property list');
}
props[idx] = newProperty;
// replace propertiesByName entry with new property
propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty;
};
DescriptorBuilder.prototype.redefineProperty = function(p) {
var nsPrefix = p.ns.prefix;
var parts = p.redefines.split('#');
var name = parseNameNs(parts[0], nsPrefix);
var attrName = parseNameNs(parts[1], name.prefix).name;
var redefinedProperty = this.propertiesByName[attrName];
if (!redefinedProperty) {
throw new Error('refined property <' + attrName + '> not found');
} else {
this.replaceProperty(redefinedProperty, p);
}
delete p.redefines;
};
DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
var ns = p.ns,
propsByName = this.propertiesByName;
if (validate) {
this.assertNotDefined(p, ns.name);
this.assertNotDefined(p, ns.localName);
}
propsByName[ns.name] = propsByName[ns.localName] = p;
};
DescriptorBuilder.prototype.removeNamedProperty = function(p) {
var ns = p.ns,
propsByName = this.propertiesByName;
delete propsByName[ns.name];
delete propsByName[ns.localName];
};
DescriptorBuilder.prototype.setBodyProperty = function(p, validate) {
if (validate && this.bodyProperty) {
throw new Error(
'body property defined multiple times ' +
'(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)');
}
this.bodyProperty = p;
};
DescriptorBuilder.prototype.addIdProperty = function(name) {
var nameNs = parseNameNs(name, this.ns.prefix);
var p = {
name: nameNs.localName,
type: 'String',
isAttr: true,
ns: nameNs
};
// ensure that id is always the first attribute (if present)
this.addProperty(p, 0);
};
DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
var propertyName = p.name,
definedProperty = this.propertiesByName[propertyName];
if (definedProperty) {
throw new Error(
'property <' + propertyName + '> already defined; ' +
'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' +
'<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
}
};
DescriptorBuilder.prototype.hasProperty = function(name) {
return this.propertiesByName[name];
};
DescriptorBuilder.prototype.addTrait = function(t) {
var allTypes = this.allTypes;
if (allTypes.indexOf(t) !== -1) {
return;
}
_.forEach(t.properties, function(p) {
// clone property to allow extensions
p = _.extend({}, p, {
name: p.ns.localName
});
Object.defineProperty(p, 'definedBy', {
value: t
});
// add redefine support
if (p.redefines) {
this.redefineProperty(p);
} else {
if (p.isBody) {
this.setBodyProperty(p);
}
this.addProperty(p);
}
}, this);
allTypes.push(t);
};
'use strict';
//var _ = require('lodash');
//
//var Types = require('./types'),
// Factory = require('./factory'),
// Registry = require('./registry'),
// Properties = require('./properties');
//
//var parseNameNs = require('./ns').parseName;
//// Moddle implementation /////////////////////////////////////////////////
/**
* @class Moddle
*
* A model that can be used to create elements of a specific type.
*
* @example
*
* var Moddle = require('moddle');
*
* var pkg = {
* name: 'mypackage',
* prefix: 'my',
* types: [
* { name: 'Root' }
* ]
* };
*
* var moddle = new Moddle([pkg]);
*
* @param {Array<Package>} packages the packages to contain
* @param {Object} options additional options to pass to the model
*/
function Moddle(packages, options) {
options = options || {};
this.properties = new Properties(this);
this.factory = new Factory(this, this.properties);
this.registry = new Registry(packages, this.properties, options);
this.typeCache = {};
}
//module.exports = Moddle;
/**
* Create an instance of the specified type.
*
* @method Moddle#create
*
* @example
*
* var foo = moddle.create('my:Foo');
* var bar = moddle.create('my:Bar', { id: 'BAR_1' });
*
* @param {String|Object} descriptor the type descriptor or name know to the model
* @param {Object} attrs a number of attributes to initialize the model instance with
* @return {Object} model instance
*/
Moddle.prototype.create = function(descriptor, attrs) {
var Type = this.getType(descriptor);
if (!Type) {
throw new Error('unknown type <' + descriptor + '>');
}
return new Type(attrs);
};
/**
* Returns the type representing a given descriptor
*
* @method Moddle#getType
*
* @example
*
* var Foo = moddle.getType('my:Foo');
* var foo = new Foo({ 'id' : 'FOO_1' });
*
* @param {String|Object} descriptor the type descriptor or name know to the model
* @return {Object} the type representing the descriptor
*/
Moddle.prototype.getType = function(descriptor) {
var cache = this.typeCache;
var name = _.isString(descriptor) ? descriptor : descriptor.ns.name;
var type = cache[name];
if (!type) {
descriptor = this.registry.getEffectiveDescriptor(name);
type = cache[descriptor.name] = this.factory.createType(descriptor);
}
return type;
};
/**
* Creates an any-element type to be used within model instances.
*
* This can be used to create custom elements that lie outside the meta-model.
* The created element contains all the meta-data required to serialize it
* as part of meta-model elements.
*
* @method Moddle#createAny
*
* @example
*
* var foo = moddle.createAny('vendor:Foo', 'http://vendor', {
* value: 'bar'
* });
*
* var container = moddle.create('my:Container', 'http://my', {
* any: [ foo ]
* });
*
* // go ahead and serialize the stuff
*
*
* @param {String} name the name of the element
* @param {String} nsUri the namespace uri of the element
* @param {Object} [properties] a map of properties to initialize the instance with
* @return {Object} the any type instance
*/
Moddle.prototype.createAny = function(name, nsUri, properties) {
var nameNs = parseNameNs(name);
var element = {
$type: name
};
var descriptor = {
name: name,
isGeneric: true,
ns: {
prefix: nameNs.prefix,
localName: nameNs.localName,
uri: nsUri
}
};
this.properties.defineDescriptor(element, descriptor);
this.properties.defineModel(element, this);
this.properties.define(element, '$parent', { enumerable: false, writable: true });
_.forEach(properties, function(a, key) {
if (_.isObject(a) && a.value !== undefined) {
element[a.name] = a.value;
} else {
element[key] = a;
}
});
return element;
};
/**
* Returns a registered package by uri or prefix
*
* @return {Object} the package
*/
Moddle.prototype.getPackage = function(uriOrPrefix) {
return this.registry.getPackage(uriOrPrefix);
};
/**
* Returns a snapshot of all known packages
*
* @return {Object} the package
*/
Moddle.prototype.getPackages = function() {
return this.registry.getPackages();
};
/**
* Returns the descriptor for an element
*/
Moddle.prototype.getElementDescriptor = function(element) {
return element.$descriptor;
};
/**
* Returns true if the given descriptor or instance
* represents the given type.
*
* May be applied to this, if element is omitted.
*/
Moddle.prototype.hasType = function(element, type) {
if (type === undefined) {
type = element;
element = this;
}
var descriptor = element.$model.getElementDescriptor(element);
return !!_.find(descriptor.allTypes, function(t) {
return t.name === type;
});
};
/**
* Returns the descriptor of an elements named property
*/
Moddle.prototype.getPropertyDescriptor = function(element, property) {
return this.getElementDescriptor(element).propertiesByName[property];
};
'use strict';
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function lower(string) {
return string.charAt(0).toLowerCase() + string.slice(1);
}
function hasLowerCaseAlias(pkg) {
return pkg.xml && pkg.xml.alias === 'lowerCase';
}
var aliasToName = function(alias, pkg) {
if (hasLowerCaseAlias(pkg)) {
return capitalize(alias);
} else {
return alias;
}
};
var nameToAlias = function(name, pkg) {
if (hasLowerCaseAlias(pkg)) {
return lower(name);
} else {
return name;
}
};
var DEFAULT_NS_MAP = {
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
};
//
//module.exports.DEFAULT_NS_MAP = {
// 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
//};
'use strict';
//var sax = require('sax'),
// _ = require('lodash');
//
//var common = require('./common'),
// Types = require('moddle').types,
// Stack = require('tiny-stack'),
// parseNameNs = require('moddle').ns.parseName,
var parseNameNs = parseName;
var aliasToName = aliasToName;
function parseNodeAttributes(node) {
var nodeAttrs = node.attributes;
return _.reduce(nodeAttrs, function(result, v, k) {
var name, ns;
if (!v.local) {
name = v.prefix;
} else {
ns = parseNameNs(v.name, v.prefix);
name = ns.name;
}
result[name] = v.value;
return result;
}, {});
}
/**
* Normalizes namespaces for a node given an optional default namespace and a
* number of mappings from uris to default prefixes.
*
* @param {XmlNode} node
* @param {Model} model the model containing all registered namespaces
* @param {Uri} defaultNsUri
*/
function normalizeNamespaces(node, model, defaultNsUri) {
var uri, childUri, prefix;
uri = node.uri || defaultNsUri;
if (uri) {
var pkg = model.getPackage(uri);
if (pkg) {
prefix = pkg.prefix;
} else {
prefix = node.prefix;
}
node.prefix = prefix;
node.uri = uri;
}
_.forEach(node.attributes, function(attr) {
normalizeNamespaces(attr, model, null);
});
}
/**
* A parse context.
*
* @class
*
* @param {ElementHandler} parseRoot the root handler for parsing a document
*/
function Context(parseRoot) {
var elementsById = {};
var references = [];
var warnings = [];
this.addReference = function(reference) {
references.push(reference);
};
this.addElement = function(id, element) {
if (!id || !element) {
throw new Error('[xml-reader] id or ctx must not be null');
}
elementsById[id] = element;
};
this.addWarning = function (w) {
warnings.push(w);
};
this.warnings = warnings;
this.references = references;
this.elementsById = elementsById;
this.parseRoot = parseRoot;
}
function BaseHandler() {}
BaseHandler.prototype.handleEnd = function() {};
BaseHandler.prototype.handleText = function() {};
BaseHandler.prototype.handleNode = function() {};
function BodyHandler() {}
BodyHandler.prototype = new BaseHandler();
BodyHandler.prototype.handleText = function(text) {
this.body = (this.body || '') + text;
};
function ReferenceHandler(property, context) {
this.property = property;
this.context = context;
}
ReferenceHandler.prototype = new BodyHandler();
ReferenceHandler.prototype.handleNode = function(node) {
if (this.element) {
throw new Error('expected no sub nodes');
} else {
this.element = this.createReference(node);
}
return this;
};
ReferenceHandler.prototype.handleEnd = function() {
this.element.id = this.body;
};
ReferenceHandler.prototype.createReference = function() {
return {
property: this.property.ns.name,
id: ''
};
};
function ValueHandler(propertyDesc, element) {
this.element = element;
this.propertyDesc = propertyDesc;
}
ValueHandler.prototype = new BodyHandler();
ValueHandler.prototype.handleEnd = function() {
var value = this.body,
element = this.element,
propertyDesc = this.propertyDesc;
value = coerceType(propertyDesc.type, value);
if (propertyDesc.isMany) {
element.get(propertyDesc.name).push(value);
} else {
element.set(propertyDesc.name, value);
}
};
function BaseElementHandler() {}
BaseElementHandler.prototype = Object.create(BodyHandler.prototype);
BaseElementHandler.prototype.handleNode = function(node) {
var parser = this;
if (!this.element) {
this.element = this.createElement(node);
var id = this.element.id;
if (id) {
this.context.addElement(id, this.element);
}
} else {
parser = this.handleChild(node);
}
return parser;
};
/**
* @class XMLReader.ElementHandler
*
*/
function ElementHandler(model, type, context) {
this.model = model;
this.type = model.getType(type);
this.context = context;
}
ElementHandler.prototype = new BaseElementHandler();
ElementHandler.prototype.addReference = function(reference) {
this.context.addReference(reference);
};
ElementHandler.prototype.handleEnd = function() {
var value = this.body,
element = this.element,
descriptor = element.$descriptor,
bodyProperty = descriptor.bodyProperty;
if (bodyProperty && value !== undefined) {
value = coerceType(bodyProperty.type, value);
element.set(bodyProperty.name, value);
}
};
/**
* Create an instance of the model from the given node.
*
* @param {Element} node the xml node
*/
ElementHandler.prototype.createElement = function(node) {
var attributes = parseNodeAttributes(node),
Type = this.type,
descriptor = Type.$descriptor,
context = this.context,
instance = new Type({});
_.forEach(attributes, function(value, name) {
var prop = descriptor.propertiesByName[name];
if (prop && prop.isReference) {
context.addReference({
element: instance,
property: prop.ns.name,
id: value
});
} else {
if (prop) {
value = coerceType(prop.type, value);
}
instance.set(name, value);
}
});
return instance;
};
ElementHandler.prototype.getPropertyForElement = function(nameNs) {
if (_.isString(nameNs)) {
nameNs = parseNameNs(nameNs);
}
var type = this.type,
model = this.model,
descriptor = type.$descriptor;
var propertyName = nameNs.name;
var property = descriptor.propertiesByName[propertyName];
// search for properties by name first
if (property) {
return property;
}
var pkg = model.getPackage(nameNs.prefix);
if (pkg) {
var typeName = nameNs.prefix + ':' + aliasToName(nameNs.localName, descriptor.$pkg),
elementType = model.getType(typeName);
// search for collection members later
property = _.find(descriptor.properties, function(p) {
return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type);
});
if (property) {
return _.extend({}, property, { effectiveType: elementType.$descriptor.name });
}
} else {
// parse unknown element (maybe extension)
property = _.find(descriptor.properties, function(p) {
return !p.isReference && !p.isAttribute && p.type === 'Element';
});
if (property) {
return property;
}
}
throw new Error('unrecognized element <' + nameNs.name + '>');
};
ElementHandler.prototype.toString = function() {
return 'ElementDescriptor[' + this.type.$descriptor.name + ']';
};
ElementHandler.prototype.valueHandler = function(propertyDesc, element) {
return new ValueHandler(propertyDesc, element);
};
ElementHandler.prototype.referenceHandler = function(propertyDesc) {
return new ReferenceHandler(propertyDesc, this.context);
};
ElementHandler.prototype.handler = function(type) {
if (type === 'Element') {
return new GenericElementHandler(this.model, type, this.context);
} else {
return new ElementHandler(this.model, type, this.context);
}
};
/**
* Handle the child element parsing
*
* @param {Element} node the xml node
*/
ElementHandler.prototype.handleChild = function(node) {
var nameNs = parseNameNs(node.local, node.prefix);
var propertyDesc, type, element, childHandler;
propertyDesc = this.getPropertyForElement(nameNs);
element = this.element;
type = propertyDesc.effectiveType || propertyDesc.type;
if (isSimple(propertyDesc.type)) {
return this.valueHandler(propertyDesc, element);
}
if (propertyDesc.isReference) {
childHandler = this.referenceHandler(propertyDesc).handleNode(node);
} else {
childHandler = this.handler(type).handleNode(node);
}
var newElement = childHandler.element;
// child handles may decide to skip elements
// by not returning anything
if (newElement !== undefined) {
if (propertyDesc.isMany) {
element.get(propertyDesc.name).push(newElement);
} else {
element.set(propertyDesc.name, newElement);
}
if (propertyDesc.isReference) {
_.extend(newElement, {
element: element
});
this.context.addReference(newElement);
} else {
// establish child -> parent relationship
newElement.$parent = element;
}
}
return childHandler;
};
function GenericElementHandler(model, type, context) {
this.model = model;
this.context = context;
}
GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype);
GenericElementHandler.prototype.createElement = function(node) {
var name = node.name,
prefix = node.prefix,
uri = node.ns[prefix],
attributes = node.attributes;
return this.model.createAny(name, uri, attributes);
};
GenericElementHandler.prototype.handleChild = function(node) {
var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node),
element = this.element;
var newElement = handler.element,
children;
if (newElement !== undefined) {
children = element.$children = element.$children || [];
children.push(newElement);
// establish child -> parent relationship
newElement.$parent = element;
}
return handler;
};
GenericElementHandler.prototype.handleText = function(text) {
this.body = this.body || '' + text;
};
GenericElementHandler.prototype.handleEnd = function() {
if (this.body) {
this.element.$body = this.body;
}
};
/**
* A reader for a meta-model
*
* @class XMLReader
*
* @param {Model} model used to read xml files
*/
function XMLReader(model) {
this.model = model;
}
XMLReader.prototype.fromXML = function(xml, rootHandler, done) {
var context = new Context(rootHandler);
var parser = sax.parser(true, { xmlns: true, trim: true });
var stackInstance = stack();
var model = this.model,
self = this;
rootHandler.context = context;
// push root handler
stackInstance.push(rootHandler);
function resolveReferences() {
var elementsById = context.elementsById;
var references = context.references;
var i, r;
for (i = 0; !!(r = references[i]); i++) {
var element = r.element;
var reference = elementsById[r.id];
var property = element.$descriptor.propertiesByName[r.property];
if (!reference) {
context.addWarning({
message: 'unresolved reference <' + r.id + '>',
element: r.element,
property: r.property,
value: r.id
});
}
if (property.isMany) {
var collection = element.get(property.name),
idx = collection.indexOf(r);
if (!reference) {
// remove unresolvable reference
collection.splice(idx, 1);
} else {
// update reference
collection[idx] = reference;
}
} else {
element.set(property.name, reference);
}
}
}
function handleClose(tagName) {
stackInstance.pop().handleEnd();
}
function handleOpen(node) {
var handler = stackInstance.peek();
normalizeNamespaces(node, model);
try {
stackInstance.push(handler.handleNode(node));
} catch (e) {
var line = this.line,
column = this.column;
throw new Error(
'unparsable content <' + node.name + '> detected\n\t' +
'line: ' + line + '\n\t' +
'column: ' + column + '\n\t' +
'nested error: ' + e.message);
}
}
function handleText(text) {
stackInstance.peek().handleText(text);
}
parser.onopentag = handleOpen;
parser.oncdata = parser.ontext = handleText;
parser.onclosetag = handleClose;
parser.onend = resolveReferences;
// deferred parse XML to make loading really ascnchronous
// this ensures the execution environment (node or browser)
// is kept responsive and that certain optimization strategies
// can kick in
_.defer(function() {
var error;
try {
parser.write(xml).close();
} catch (e) {
error = e;
}
done(error, error ? undefined : rootHandler.element, context);
});
};
XMLReader.prototype.handler = function(name) {
return new ElementHandler(this.model, name);
};
'use strict';
//var _ = require('lodash');
//var Types = require('moddle').types,
// common = require('./common'),
var parseNameNs = parseName;
var nameToAlias = nameToAlias;
var XML_PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>\n';
var CDATA_ESCAPE = /[<>"&]+/;
var DEFAULT_NS_MAP = DEFAULT_NS_MAP;
function nsName(ns) {
if (_.isString(ns)) {
return ns;
} else {
return (ns.prefix ? ns.prefix + ':' : '') + ns.localName;
}
}
function getElementNs(ns, descriptor) {
if (descriptor.isGeneric) {
return descriptor.name;
} else {
return _.extend({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns);
}
}
function getPropertyNs(ns, descriptor) {
return _.extend({ localName: descriptor.ns.localName }, ns);
}
function getSerializableProperties(element) {
var descriptor = element.$descriptor;
return _.filter(descriptor.properties, function(p) {
var name = p.name;
// do not serialize defaults
if (!element.hasOwnProperty(name)) {
return false;
}
var value = element[name];
// do not serialize default equals
if (value === p.default) {
return false;
}
return p.isMany ? value.length : true;
});
}
/**
* Escape a string attribute to not contain any bad values (line breaks, '"', ...)
*
* @param {String} str the string to escape
* @return {String} the escaped string
*/
function escapeAttr(str) {
var escapeMap = {
'\n': ' ',
'\n\r': ' ',
'"': '"'
};
// ensure we are handling strings here
str = _.isString(str) ? str : '' + str;
return str.replace(/(\n|\n\r|")/g, function(str) {
return escapeMap[str];
});
}
function filterAttributes(props) {
return _.filter(props, function(p) { return p.isAttr; });
}
function filterContained(props) {
return _.filter(props, function(p) { return !p.isAttr; });
}
function ReferenceSerializer(parent, ns) {
this.ns = ns;
}
ReferenceSerializer.prototype.build = function(element) {
this.element = element;
return this;
};
ReferenceSerializer.prototype.serializeTo = function(writer) {
writer
.appendIndent()
.append('<' + nsName(this.ns) + '>' + this.element.id + '</' + nsName(this.ns) + '>')
.appendNewLine();
};
function BodySerializer() {}
BodySerializer.prototype.serializeValue = BodySerializer.prototype.serializeTo = function(writer) {
var value = this.value,
escape = this.escape;
if (escape) {
writer.append('<![CDATA[');
}
writer.append(this.value);
if (escape) {
writer.append(']]>');
}
};
BodySerializer.prototype.build = function(prop, value) {
this.value = value;
if (prop.type === 'String' && CDATA_ESCAPE.test(value)) {
this.escape = true;
}
return this;
};
function ValueSerializer(ns) {
this.ns = ns;
}
ValueSerializer.prototype = new BodySerializer();
ValueSerializer.prototype.serializeTo = function(writer) {
writer
.appendIndent()
.append('<' + nsName(this.ns) + '>');
this.serializeValue(writer);
writer
.append( '</' + nsName(this.ns) + '>')
.appendNewLine();
};
function ElementSerializer(parent, ns) {
this.body = [];
this.attrs = [];
this.parent = parent;
this.ns = ns;
}
ElementSerializer.prototype.build = function(element) {
this.element = element;
var otherAttrs = this.parseNsAttributes(element);
if (!this.ns) {
this.ns = this.nsTagName(element.$descriptor);
}
if (element.$descriptor.isGeneric) {
this.parseGeneric(element);
} else {
var properties = getSerializableProperties(element);
this.parseAttributes(filterAttributes(properties));
this.parseContainments(filterContained(properties));
this.parseGenericAttributes(element, otherAttrs);
}
return this;
};
ElementSerializer.prototype.nsTagName = function(descriptor) {
var effectiveNs = this.logNamespaceUsed(descriptor.ns);
return getElementNs(effectiveNs, descriptor);
};
ElementSerializer.prototype.nsPropertyTagName = function(descriptor) {
var effectiveNs = this.logNamespaceUsed(descriptor.ns);
return getPropertyNs(effectiveNs, descriptor);
};
ElementSerializer.prototype.isLocalNs = function(ns) {
return ns.uri === this.ns.uri;
};
ElementSerializer.prototype.nsAttributeName = function(element) {
var ns;
if (_.isString(element)) {
ns = parseNameNs(element);
} else
if (element.ns) {
ns = element.ns;
}
var effectiveNs = this.logNamespaceUsed(ns);
// strip prefix if same namespace like parent
if (this.isLocalNs(effectiveNs)) {
return { localName: ns.localName };
} else {
return _.extend({ localName: ns.localName }, effectiveNs);
}
};
ElementSerializer.prototype.parseGeneric = function(element) {
var self = this,
body = this.body,
attrs = this.attrs;
_.forEach(element, function(val, key) {
if (key === '$body') {
body.push(new BodySerializer().build({ type: 'String' }, val));
} else
if (key === '$children') {
_.forEach(val, function(child) {
body.push(new ElementSerializer(self).build(child));
});
} else
if (key.indexOf('$') !== 0) {
attrs.push({ name: key, value: escapeAttr(val) });
}
});
};
/**
* Parse namespaces and return a list of left over generic attributes
*
* @param {Object} element
* @return {Array<Object>}
*/
ElementSerializer.prototype.parseNsAttributes = function(element) {
var self = this;
var genericAttrs = element.$attrs;
var attributes = [];
// parse namespace attributes first
// and log them. push non namespace attributes to a list
// and process them later
_.forEach(genericAttrs, function(value, name) {
var nameNs = parseNameNs(name);
if (nameNs.prefix === 'xmlns') {
self.logNamespace({ prefix: nameNs.localName, uri: value });
} else
if (!nameNs.prefix && nameNs.localName === 'xmlns') {
self.logNamespace({ uri: value });
} else {
attributes.push({ name: name, value: value });
}
});
return attributes;
};
ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) {
var self = this;
_.forEach(attributes, function(attr) {
try {
self.addAttribute(self.nsAttributeName(attr.name), attr.value);
} catch (e) {
console.warn('[writer] missing namespace information for ', attr.name, '=', attr.value, 'on', element, e);
}
});
};
ElementSerializer.prototype.parseContainments = function(properties) {
var self = this,
body = this.body,
element = this.element,
typeDesc = element.$descriptor;
_.forEach(properties, function(p) {
var value = element.get(p.name),
isReference = p.isReference,
isMany = p.isMany;
var ns = self.nsPropertyTagName(p);
if (!isMany) {
value = [ value ];
}
if (p.isBody) {
body.push(new BodySerializer().build(p, value[0]));
} else
//if (Types.isSimple(p.type)) {
if (isSimple(p.type)) {
_.forEach(value, function(v) {
body.push(new ValueSerializer(ns).build(p, v));
});
} else
if (isReference) {
_.forEach(value, function(v) {
body.push(new ReferenceSerializer(self, ns).build(v));
});
} else {
// allow serialization via type
// rather than element name
var asType = p.serialize === 'xsi:type';
_.forEach(value, function(v) {
var serializer;
if (asType) {
serializer = new TypeSerializer(self, ns);
} else {
serializer = new ElementSerializer(self);
}
body.push(serializer.build(v));
});
}
});
};
ElementSerializer.prototype.getNamespaces = function() {
if (!this.parent) {
if (!this.namespaces) {
this.namespaces = {
prefixMap: {},
uriMap: {},
used: {}
};
}
} else {
this.namespaces = this.parent.getNamespaces();
}
return this.namespaces;
};
ElementSerializer.prototype.logNamespace = function(ns) {
var namespaces = this.getNamespaces();
var existing = namespaces.uriMap[ns.uri];
if (!existing) {
namespaces.uriMap[ns.uri] = ns;
}
namespaces.prefixMap[ns.prefix] = ns.uri;
return ns;
};
ElementSerializer.prototype.logNamespaceUsed = function(ns) {
var element = this.element,
model = element.$model,
namespaces = this.getNamespaces();
// ns may be
//
// * prefix only
// * prefix:uri
var prefix = ns.prefix;
var uri = ns.uri || DEFAULT_NS_MAP[prefix] ||
namespaces.prefixMap[prefix] || (model ? (model.getPackage(prefix) || {}).uri : null);
if (!uri) {
throw new Error('no namespace uri given for prefix <' + ns.prefix + '>');
}
ns = namespaces.uriMap[uri];
if (!ns) {
ns = this.logNamespace({ prefix: prefix, uri: uri });
}
if (!namespaces.used[ns.uri]) {
namespaces.used[ns.uri] = ns;
}
return ns;
};
ElementSerializer.prototype.parseAttributes = function(properties) {
var self = this,
element = this.element;
_.forEach(properties, function(p) {
self.logNamespaceUsed(p.ns);
var value = element.get(p.name);
if (p.isReference) {
value = value.id;
}
self.addAttribute(self.nsAttributeName(p), value);
});
};
ElementSerializer.prototype.addAttribute = function(name, value) {
var attrs = this.attrs;
if (_.isString(value)) {
value = escapeAttr(value);
}
attrs.push({ name: name, value: value });
};
ElementSerializer.prototype.serializeAttributes = function(writer) {
var element = this.element,
attrs = this.attrs,
root = !this.parent,
namespaces = this.namespaces;
function collectNsAttrs() {
return _.collect(namespaces.used, function(ns) {
var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : '');
return { name: name, value: ns.uri };
});
}
if (root) {
attrs = collectNsAttrs().concat(attrs);
}
_.forEach(attrs, function(a) {
writer
.append(' ')
.append(nsName(a.name)).append('="').append(a.value).append('"');
});
};
ElementSerializer.prototype.serializeTo = function(writer) {
var hasBody = this.body.length;
writer
.appendIndent()
.append('<' + nsName(this.ns));
this.serializeAttributes(writer);
writer
.append(hasBody ? '>' : ' />')
.appendNewLine();
writer.indent();
_.forEach(this.body, function(b) {
b.serializeTo(writer);
});
writer.unindent();
if (hasBody) {
writer
.appendIndent()
.append('</' + nsName(this.ns) + '>')
.appendNewLine();
}
};
/**
* A serializer for types that handles serialization of data types
*/
function TypeSerializer(parent, ns) {
ElementSerializer.call(this, parent, ns);
}
TypeSerializer.prototype = new ElementSerializer();
TypeSerializer.prototype.build = function(element) {
this.element = element;
this.typeNs = this.nsTagName(element.$descriptor);
return ElementSerializer.prototype.build.call(this, element);
};
TypeSerializer.prototype.isLocalNs = function(ns) {
return ns.uri === this.typeNs.uri;
};
function SavingWriter() {
this.value = '';
this.write = function(str) {
this.value += str;
};
}
function FormatingWriter(out, format) {
var indent = [''];
this.append = function(str) {
out.write(str);
return this;
};
this.appendNewLine = function() {
if (format) {
out.write('\n');
}
return this;
};
this.appendIndent = function() {
if (format) {
out.write(indent.join(' '));
}
return this;
};
this.indent = function() {
indent.push('');
return this;
};
this.unindent = function() {
indent.pop();
return this;
};
}
/**
* A writer for meta-model backed document trees
*
* @class XMLWriter
*/
function XMLWriter(options) {
options = _.extend({ format: false, preamble: true }, options || {});
function toXML(tree, writer) {
var internalWriter = writer || new SavingWriter();
var formatingWriter = new FormatingWriter(internalWriter, options.format);
if (options.preamble) {
formatingWriter.append(XML_PREAMBLE);
}
new ElementSerializer().build(tree).serializeTo(formatingWriter);
if (!writer) {
return internalWriter.value;
}
}
return {
toXML: toXML
};
}
//module.exports = XMLWriter;
'use strict';
//
//var _ = require('lodash');
//
//var Moddle = require('bpmn-moddle');
//ModdleXml = require('moddle-xml');
//
function createModel(packages) {
return new Moddle(packages);
}
/**
* A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
*
* @class BpmnModdle
* @extends Moddle
*
* @param {Object|Array} packages to use for instantiating the model
* @param {Object} [options] additional options to pass over
*/
function BpmnModdle(packages, options) {
var packages = {
bpmn: {
"name": "BPMN20",
"uri": "http://www.omg.org/spec/BPMN/20100524/MODEL",
"associations": [],
"types": [
{
"name": "Interface",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "operations",
"type": "Operation",
"association": "A_operations_interface",
"isMany": true
},
{
"name": "implementationRef",
"type": "String",
"isAttr": true
}
]
},
{
"name": "Operation",