123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- import {
- assign,
- forEach,
- bind
- } from 'min-dash';
- import {
- isBuiltIn as isBuiltInType
- } from './types';
- import DescriptorBuilder from './descriptor-builder';
- import {
- parseName as parseNameNs
- } from './ns';
- /**
- * A registry of Moddle packages.
- *
- * @param {Array<Package>} packages
- * @param {Properties} properties
- */
- export default function Registry(packages, properties) {
- this.packageMap = {};
- this.typeMap = {};
- this.packages = [];
- this.properties = properties;
- forEach(packages, bind(this.registerPackage, this));
- }
- Registry.prototype.getPackage = function(uriOrPrefix) {
- return this.packageMap[uriOrPrefix];
- };
- Registry.prototype.getPackages = function() {
- return this.packages;
- };
- Registry.prototype.registerPackage = function(pkg) {
- // copy package
- pkg = assign({}, pkg);
- var pkgMap = this.packageMap;
- ensureAvailable(pkgMap, pkg, 'prefix');
- ensureAvailable(pkgMap, pkg, 'uri');
- // register types
- forEach(pkg.types, bind(function(descriptor) {
- this.registerType(descriptor, pkg);
- }, this));
- pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg;
- this.packages.push(pkg);
- };
- /**
- * Register a type from a specific package with us
- */
- Registry.prototype.registerType = function(type, pkg) {
- type = assign({}, type, {
- superClass: (type.superClass || []).slice(),
- extends: (type.extends || []).slice(),
- properties: (type.properties || []).slice(),
- meta: assign(({}, type.meta || {}))
- });
- var ns = parseNameNs(type.name, pkg.prefix),
- name = ns.name,
- propertiesByName = {};
- // parse properties
- forEach(type.properties, bind(function(p) {
- // namespace property names
- var propertyNs = parseNameNs(p.name, ns.prefix),
- propertyName = propertyNs.name;
- // namespace property types
- if (!isBuiltInType(p.type)) {
- p.type = parseNameNs(p.type, propertyNs.prefix).name;
- }
- assign(p, {
- ns: propertyNs,
- name: propertyName
- });
- propertiesByName[propertyName] = p;
- }, this));
- // update ns + name
- assign(type, {
- ns: ns,
- name: name,
- propertiesByName: propertiesByName
- });
- forEach(type.extends, bind(function(extendsName) {
- var extended = this.typeMap[extendsName];
- extended.traits = extended.traits || [];
- extended.traits.push(name);
- }, this));
- // link to package
- this.definePackage(type, pkg);
- // register
- this.typeMap[name] = type;
- };
- /**
- * Traverse the type hierarchy from bottom to top,
- * calling iterator with (type, inherited) for all elements in
- * the inheritance chain.
- *
- * @param {Object} nsName
- * @param {Function} iterator
- * @param {Boolean} [trait=false]
- */
- Registry.prototype.mapTypes = function(nsName, iterator, trait) {
- var type = isBuiltInType(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name];
- var self = this;
- /**
- * Traverse the selected trait.
- *
- * @param {String} cls
- */
- function traverseTrait(cls) {
- return traverseSuper(cls, true);
- }
- /**
- * Traverse the selected super type or trait
- *
- * @param {String} cls
- * @param {Boolean} [trait=false]
- */
- function traverseSuper(cls, trait) {
- var parentNs = parseNameNs(cls, isBuiltInType(cls) ? '' : nsName.prefix);
- self.mapTypes(parentNs, iterator, trait);
- }
- if (!type) {
- throw new Error('unknown type <' + nsName.name + '>');
- }
- forEach(type.superClass, trait ? traverseTrait : traverseSuper);
- // call iterator with (type, inherited=!trait)
- iterator(type, !trait);
- forEach(type.traits, traverseTrait);
- };
- /**
- * 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 nsName = parseNameNs(name);
- var builder = new DescriptorBuilder(nsName);
- this.mapTypes(nsName, function(type, inherited) {
- builder.addTrait(type, inherited);
- });
- 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 });
- };
- ///////// helpers ////////////////////////////
- function ensureAvailable(packageMap, pkg, identifierKey) {
- var value = pkg[identifierKey];
- if (value in packageMap) {
- throw new Error('package with ' + identifierKey + ' <' + value + '> already defined');
- }
- }
|