[Containment] Add general policy for containment rules

Add general policy for supporting containment rules, WTD-962.
This commit is contained in:
Victor Woeltjen 2015-04-07 19:58:39 -07:00
parent 3c3dd0ad17
commit 3c00eb86ea
4 changed files with 182 additions and 0 deletions

View File

@ -0,0 +1,2 @@
Implements support for rules which determine which objects are allowed
to contain other objects, typically by type.

View File

@ -0,0 +1,53 @@
/*global define*/
define(
[],
function () {
"use strict";
/**
* Build a table indicating which types are expected to expose
* which capabilities.
*/
function CapabilityTable(typeService, capabilityService) {
var table = {};
// Build an initial model for a type
function buildModel(type) {
var model = Object.create(type.getInitialModel() || {});
model.type = type.getKey();
return model;
}
// Get capabilities expected for this type
function getCapabilities(type) {
return capabilityService.getCapabilities(buildModel(type));
}
// Populate the lookup table for this type's capabilities
function addToTable(type) {
var typeKey = type.getKey();
Object.keys(getCapabilities(type)).forEach(function (key) {
table[key] = table[key] || {};
table[key][typeKey] = true;
});
}
// Build the table
(typeService.listTypes() || []).forEach(addToTable);
return {
/**
* Check if a type is expected to expose a specific
* capability.
*/
hasCapability: function (typeKey, capabilityKey) {
return (table[capabilityKey] || {})[typeKey];
}
};
}
return CapabilityTable;
}
);

View File

@ -0,0 +1,29 @@
/*global define*/
define(
['./ContainmentTable'],
function (ContainmentTable) {
"use strict";
/**
* Defines composition policy as driven by type metadata.
*/
function CompositionPolicy(typeService, capabilityService) {
// We're really just wrapping the containment table and rephrasing
// it as a policy decision.
var table = new ContainmentTable(typeService, capabilityService);
return {
/**
* Is the type identified by the candidate allowed to
* contain the type described by the context?
*/
allow: function (candidate, context) {
return table.canContain(candidate, context);
}
};
}
return CompositionPolicy;
}
);

View File

@ -0,0 +1,98 @@
/*global define*/
define(
['./CapabilityTable'],
function (CapabilityTable) {
"use strict";
// Symbolic value for the type table for cases when any type
// is allowed to be contained.
var ANY = true;
/**
* Supports composition policy by maintaining a table of
* domain object types, to determine if they can contain
* other domain object types. This is determined at application
* start time (plug-in support means this cannot be determined
* prior to that, but we don't want to redo these calculations
* every time policy is checked.)
*/
function ContainmentTable(typeService, capabilityService) {
var types = typeService.listTypes(),
capabilityTable = new CapabilityTable(typeService, capabilityService),
table = {};
// Check if one type can contain another
function canContain(containerType, containedType) {
}
// Add types which have all these capabilities to the set
// of allowed types
function addToSetByCapability(set, has) {
has = Array.isArray(has) ? has : [has];
types.forEach(function (type) {
var typeKey = type.getKey();
set[typeKey] = has.map(function (capabilityKey) {
return capabilityTable.hasCapability(typeKey, capabilityKey);
}).reduce(function (a, b) {
return a && b;
}, true);
});
}
// Add this type (or type description) to the set of allowed types
function addToSet(set, type) {
// Is this a simple case of an explicit type identifier?
if (typeof type === 'string') {
// If so, add it to the set of allowed types
set[type] = true;
} else {
// Otherwise, populate that set based on capabilities
addToSetByCapability(set, (type || {}).has || []);
}
}
// Add to the lookup table for this type
function addToTable(type) {
var key = type.getKey(),
definition = type.getDefinition() || {},
contains = definition.contains;
// Check for defined containment restrictions
if (contains === undefined) {
// If not, accept anything
table[key] = ANY;
} else {
// Start with an empty set...
table[key] = {};
// ...cast accepted types to array if necessary...
contains = Array.isArray(contains) ? contains : [contains];
// ...and add all containment rules to that set
contains.forEach(function (c) {
addToSet(table[key], c);
});
}
}
// Build the table
types.forEach(addToTable);
return {
/**
* Check if domain objects of one type can contain domain
* objects of another type.
* @returns {boolean} true if allowable
*/
canContain: function (containerType, containedType) {
var set = table[containerType.getKey()] || {};
// Recognize either the symbolic value for "can contain
// anything", or lookup the specific type from the set.
return (set === ANY) || set[containedType.getKey()];
}
};
}
return ContainmentTable;
}
);