From 65325b90fd0272144f17bed9a3530e79f543896d Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Tue, 21 Feb 2017 12:14:09 -0800 Subject: [PATCH] Composition policy takes child instance The composition policy now takes a child instance instead of the child type, as in all cases we have access to the child object. This allows new-style objects to be contained by old-style objects. Updated all composition policies to use standardized argument names instead of `context` and `candidate`; this makes it easier to understand. Updated AddActionProvider to hardcode the object types supported. --- docs/src/guide/index.md | 5 +- .../edit/src/creation/AddActionProvider.js | 4 +- .../edit/src/creation/CreateWizard.js | 14 +-- platform/containment/bundle.js | 3 - platform/containment/src/CapabilityTable.js | 77 ------------ .../containment/src/ComposeActionPolicy.js | 6 +- platform/containment/src/CompositionPolicy.js | 44 ++++--- platform/containment/src/ContainmentTable.js | 116 ------------------ .../containment/test/CapabilityTableSpec.js | 85 ------------- .../containment/test/ContainmentTableSpec.js | 96 --------------- .../entanglement/src/services/CopyService.js | 2 +- .../entanglement/src/services/LinkService.js | 2 +- .../entanglement/src/services/MoveService.js | 2 +- .../layout/src/LayoutCompositionPolicy.js | 15 +-- .../policies/AdapterCompositionPolicy.js | 11 +- 15 files changed, 56 insertions(+), 426 deletions(-) delete mode 100644 platform/containment/src/CapabilityTable.js delete mode 100644 platform/containment/src/ContainmentTable.js delete mode 100644 platform/containment/test/CapabilityTableSpec.js delete mode 100644 platform/containment/test/ContainmentTableSpec.js diff --git a/docs/src/guide/index.md b/docs/src/guide/index.md index 4e8684fc32..369ad05144 100644 --- a/docs/src/guide/index.md +++ b/docs/src/guide/index.md @@ -2261,10 +2261,7 @@ The platform understands the following policy categories (specifiable as the * `action`: Determines whether or not a given action is allowable. The candidate argument here is an Action; the context is its action context object. -* `composition`: Determines whether or not domain objects of a given type are -allowed to contain domain objects of another type. The candidate argument here -is the container's `Type`; the context argument is the `Type` of the object to be -contained. +* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`). * `view`: Determines whether or not a view is applicable for a domain object. The candidate argument is the view's extension definition; the context argument is the `DomainObject` to be viewed. diff --git a/platform/commonUI/edit/src/creation/AddActionProvider.js b/platform/commonUI/edit/src/creation/AddActionProvider.js index 582d7627d8..21666680d3 100644 --- a/platform/commonUI/edit/src/creation/AddActionProvider.js +++ b/platform/commonUI/edit/src/creation/AddActionProvider.js @@ -66,9 +66,7 @@ define( } // Introduce one create action per type - return this.typeService.listTypes().filter(function (type) { - return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type); - }).map(function (type) { + ['timeline', 'activity'].map(function (type) { return new AddAction( type, destination, diff --git a/platform/commonUI/edit/src/creation/CreateWizard.js b/platform/commonUI/edit/src/creation/CreateWizard.js index 6921ca8669..9e3499690e 100644 --- a/platform/commonUI/edit/src/creation/CreateWizard.js +++ b/platform/commonUI/edit/src/creation/CreateWizard.js @@ -56,16 +56,16 @@ define( */ CreateWizard.prototype.getFormStructure = function (includeLocation) { var sections = [], - type = this.type, + domainObject = this.domainObject, policyService = this.policyService; - function validateLocation(locatingObject) { - var locatingType = locatingObject && - locatingObject.getCapability('type'); - return locatingType && policyService.allow( + function validateLocation(parent) { + var parentType = parent && + parent.getCapability('type'); + return parentType && policyService.allow( "composition", - locatingType, - type + parentType, + domainObject ); } diff --git a/platform/containment/bundle.js b/platform/containment/bundle.js index d77697e8dc..a64710b974 100644 --- a/platform/containment/bundle.js +++ b/platform/containment/bundle.js @@ -40,9 +40,6 @@ define([ { "category": "composition", "implementation": CompositionPolicy, - "depends": [ - "$injector" - ], "message": "Objects of this type cannot contain objects of that type." }, { diff --git a/platform/containment/src/CapabilityTable.js b/platform/containment/src/CapabilityTable.js deleted file mode 100644 index 917b3bd48d..0000000000 --- a/platform/containment/src/CapabilityTable.js +++ /dev/null @@ -1,77 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - - -define( - [], - function () { - - /** - * Build a table indicating which types are expected to expose - * which capabilities. This supports composition policy (rules - * for which objects can contain which other objects) which - * sometimes is determined based on the presence of capabilities. - * @constructor - * @memberof platform/containment - */ - function CapabilityTable(typeService, capabilityService) { - var self = this; - - // 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) { - self.table[key] = self.table[key] || {}; - self.table[key][typeKey] = true; - }); - } - - // Build the table - this.table = {}; - (typeService.listTypes() || []).forEach(addToTable); - } - - /** - * Check if a type is expected to expose a specific capability. - * @param {string} typeKey the type identifier - * @param {string} capabilityKey the capability identifier - * @returns {boolean} true if expected to be exposed - */ - CapabilityTable.prototype.hasCapability = function (typeKey, capabilityKey) { - return (this.table[capabilityKey] || {})[typeKey]; - }; - - return CapabilityTable; - } -); diff --git a/platform/containment/src/ComposeActionPolicy.js b/platform/containment/src/ComposeActionPolicy.js index a1e36fd024..0a107756af 100644 --- a/platform/containment/src/ComposeActionPolicy.js +++ b/platform/containment/src/ComposeActionPolicy.js @@ -45,9 +45,7 @@ define( ComposeActionPolicy.prototype.allowComposition = function (containerObject, selectedObject) { // Get the object types involved in the compose action var containerType = containerObject && - containerObject.getCapability('type'), - selectedType = selectedObject && - selectedObject.getCapability('type'); + containerObject.getCapability('type'); // Get a reference to the policy service if needed... this.policyService = this.policyService || this.getPolicyService(); @@ -57,7 +55,7 @@ define( this.policyService.allow( 'composition', containerType, - selectedType + selectedObject ); }; diff --git a/platform/containment/src/CompositionPolicy.js b/platform/containment/src/CompositionPolicy.js index 1e69fa64cb..16dda80eb3 100644 --- a/platform/containment/src/CompositionPolicy.js +++ b/platform/containment/src/CompositionPolicy.js @@ -26,8 +26,8 @@ * @namespace platform/containment */ define( - ['./ContainmentTable'], - function (ContainmentTable) { + [], + function () { /** * Defines composition policy as driven by type metadata. @@ -35,21 +35,35 @@ define( * @memberof platform/containment * @implements {Policy.} */ - function CompositionPolicy($injector) { - // We're really just wrapping the containment table and rephrasing - // it as a policy decision. - var table; - - this.getTable = function () { - return (table = table || new ContainmentTable( - $injector.get('typeService'), - $injector.get('capabilityService') - )); - }; + function CompositionPolicy() { } - CompositionPolicy.prototype.allow = function (candidate, context) { - return this.getTable().canContain(candidate, context); + CompositionPolicy.prototype.allow = function (parentType, child) { + var childType = child.getCapability('type'); + var childTypeKey = childType.getKey(); + var parentDef = parent.getDefinition(); + + // A parent without containment rules can contain anything. + if (!parentDef.contains) { + return true; + } + + // If any containment rule matches context type, the candidate + // can contain this type. + return parentDef.contains.some(function (c) { + // Simple containment rules are supported typeKeys. + if (typeof c === 'string') { + return c === childTypeKey; + } + // More complicated rules require context to have all specified + // capabilities. + if (!Array.isArray(c.has)) { + c.has = [c.has]; + } + return c.has.every(function (capability) { + return child.hasCapability(capability); + }); + }); }; return CompositionPolicy; diff --git a/platform/containment/src/ContainmentTable.js b/platform/containment/src/ContainmentTable.js deleted file mode 100644 index 17bc69db2d..0000000000 --- a/platform/containment/src/ContainmentTable.js +++ /dev/null @@ -1,116 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define( - ['./CapabilityTable'], - function (CapabilityTable) { - - // 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.) - * @constructor - * @memberof platform/containment - */ - function ContainmentTable(typeService, capabilityService) { - var self = this, - types = typeService.listTypes(), - capabilityTable = new CapabilityTable(typeService, capabilityService); - - // 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 - self.table[key] = ANY; - } else { - // Start with an empty set... - self.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(self.table[key], c); - }); - } - } - - // Build the table - this.table = {}; - types.forEach(addToTable); - } - - /** - * Check if domain objects of one type can contain domain - * objects of another type. - * @param {Type} containerType type of the containing domain object - * @param {Type} containedType type of the domain object - * to be contained - * @returns {boolean} true if allowable - */ - ContainmentTable.prototype.canContain = function (containerType, containedType) { - var set = this.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; - } -); diff --git a/platform/containment/test/CapabilityTableSpec.js b/platform/containment/test/CapabilityTableSpec.js deleted file mode 100644 index 646ef82882..0000000000 --- a/platform/containment/test/CapabilityTableSpec.js +++ /dev/null @@ -1,85 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define( - ["../src/CapabilityTable"], - function (CapabilityTable) { - describe("Composition policy's capability table", function () { - var mockTypeService, - mockCapabilityService, - mockTypes, - table; - - beforeEach(function () { - mockTypeService = jasmine.createSpyObj( - 'typeService', - ['listTypes'] - ); - mockCapabilityService = jasmine.createSpyObj( - 'capabilityService', - ['getCapabilities'] - ); - // Both types can only contain b, let's say - mockTypes = ['a', 'b'].map(function (type) { - var mockType = jasmine.createSpyObj( - 'type-' + type, - ['getKey', 'getDefinition', 'getInitialModel'] - ); - mockType.getKey.andReturn(type); - // Return a model to drive apparent capabilities - mockType.getInitialModel.andReturn({ id: type }); - return mockType; - }); - - mockTypeService.listTypes.andReturn(mockTypes); - mockCapabilityService.getCapabilities.andCallFake(function (model) { - var capabilities = {}; - capabilities[model.id + '-capability'] = true; - return capabilities; - }); - - table = new CapabilityTable( - mockTypeService, - mockCapabilityService - ); - }); - - it("provides for lookup of capabilities by type", function () { - // Based on initial model, should report the presence - // of particular capabilities - suffixed above with -capability - expect(table.hasCapability('a', 'a-capability')) - .toBeTruthy(); - expect(table.hasCapability('a', 'b-capability')) - .toBeFalsy(); - expect(table.hasCapability('a', 'c-capability')) - .toBeFalsy(); - expect(table.hasCapability('b', 'a-capability')) - .toBeFalsy(); - expect(table.hasCapability('b', 'b-capability')) - .toBeTruthy(); - expect(table.hasCapability('b', 'c-capability')) - .toBeFalsy(); - }); - - }); - } -); diff --git a/platform/containment/test/ContainmentTableSpec.js b/platform/containment/test/ContainmentTableSpec.js deleted file mode 100644 index a83d9c1c82..0000000000 --- a/platform/containment/test/ContainmentTableSpec.js +++ /dev/null @@ -1,96 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT is licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * Open MCT includes source code licensed under additional open source - * licenses. See the Open Source Licenses file (LICENSES.md) included with - * this source code distribution or the Licensing information page available - * at runtime from the About dialog for additional information. - *****************************************************************************/ - -define( - ["../src/ContainmentTable"], - function (ContainmentTable) { - describe("Composition policy's containment table", function () { - var mockTypeService, - mockCapabilityService, - mockTypes, - table; - - beforeEach(function () { - mockTypeService = jasmine.createSpyObj( - 'typeService', - ['listTypes'] - ); - mockCapabilityService = jasmine.createSpyObj( - 'capabilityService', - ['getCapabilities'] - ); - // Both types can only contain b, let's say - mockTypes = ['a', 'b', 'c'].map(function (type, index) { - var mockType = jasmine.createSpyObj( - 'type-' + type, - ['getKey', 'getDefinition', 'getInitialModel'] - ); - mockType.getKey.andReturn(type); - mockType.getDefinition.andReturn({ - // First two contain objects with capability 'b'; - // third one defines no containership rules - contains: (index < 2) ? [{ has: 'b' }] : undefined - }); - // Return a model to drive apparent capabilities - mockType.getInitialModel.andReturn({ id: type }); - return mockType; - }); - - mockTypeService.listTypes.andReturn(mockTypes); - mockCapabilityService.getCapabilities.andCallFake(function (model) { - var capabilities = {}; - capabilities[model.id] = true; - return capabilities; - }); - - table = new ContainmentTable( - mockTypeService, - mockCapabilityService - ); - }); - - // The plain type case is tested in CompositionPolicySpec, - // so just test for special syntax ('has', or no contains rules) here - it("enforces 'has' containment rules related to capabilities", function () { - expect(table.canContain(mockTypes[0], mockTypes[1])) - .toBeTruthy(); - expect(table.canContain(mockTypes[1], mockTypes[1])) - .toBeTruthy(); - expect(table.canContain(mockTypes[1], mockTypes[0])) - .toBeFalsy(); - expect(table.canContain(mockTypes[0], mockTypes[0])) - .toBeFalsy(); - }); - - it("allows anything when no containership rules are defined", function () { - expect(table.canContain(mockTypes[2], mockTypes[0])) - .toBeTruthy(); - expect(table.canContain(mockTypes[2], mockTypes[1])) - .toBeTruthy(); - expect(table.canContain(mockTypes[2], mockTypes[2])) - .toBeTruthy(); - }); - - - }); - } -); diff --git a/platform/entanglement/src/services/CopyService.js b/platform/entanglement/src/services/CopyService.js index fe6a5a43ea..2ac29268cd 100644 --- a/platform/entanglement/src/services/CopyService.js +++ b/platform/entanglement/src/services/CopyService.js @@ -48,7 +48,7 @@ define( return this.policyService.allow( "composition", parentCandidate.getCapability('type'), - object.getCapability('type') + object ); }; diff --git a/platform/entanglement/src/services/LinkService.js b/platform/entanglement/src/services/LinkService.js index 74c985f312..4942f93d35 100644 --- a/platform/entanglement/src/services/LinkService.js +++ b/platform/entanglement/src/services/LinkService.js @@ -52,7 +52,7 @@ define( return this.policyService.allow( "composition", parentCandidate.getCapability('type'), - object.getCapability('type') + object ); }; diff --git a/platform/entanglement/src/services/MoveService.js b/platform/entanglement/src/services/MoveService.js index 592092529f..82c3a70371 100644 --- a/platform/entanglement/src/services/MoveService.js +++ b/platform/entanglement/src/services/MoveService.js @@ -56,7 +56,7 @@ define( return this.policyService.allow( "composition", parentCandidate.getCapability('type'), - object.getCapability('type') + object ); }; diff --git a/platform/features/layout/src/LayoutCompositionPolicy.js b/platform/features/layout/src/LayoutCompositionPolicy.js index 4f4d01b81a..a281d2d2ea 100644 --- a/platform/features/layout/src/LayoutCompositionPolicy.js +++ b/platform/features/layout/src/LayoutCompositionPolicy.js @@ -34,13 +34,14 @@ define( function LayoutCompositionPolicy() { } - LayoutCompositionPolicy.prototype.allow = function (candidate, context) { - var isFolderInLayout = - candidate && - context && - candidate.instanceOf('layout') && - context.instanceOf('folder'); - return !isFolderInLayout; + LayoutCompositionPolicy.prototype.allow = function (parentType, child) { + if (parentType.instanceOf('layout') && + child.getCapability('type').instanceOf('folder')) { + + return false; + } + + return true; }; return LayoutCompositionPolicy; diff --git a/src/adapter/policies/AdapterCompositionPolicy.js b/src/adapter/policies/AdapterCompositionPolicy.js index 865b08cb59..b9d196e6f0 100644 --- a/src/adapter/policies/AdapterCompositionPolicy.js +++ b/src/adapter/policies/AdapterCompositionPolicy.js @@ -26,15 +26,14 @@ define([], function () { } AdapterCompositionPolicy.prototype.allow = function ( - containerType, - childType + parentType, + child ) { - var containerObject = containerType.getInitialModel(); - var childObject = childType.getInitialModel(); + var container = parentType.getInitialModel(); return this.openmct.composition.checkPolicy( - containerObject, - childObject + container, + child ); };