-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[INTERNAL] Core: move registries from Core to Component and Element
Introduces a new mixin "ManagedObjectRegistry" that can be applied to a ManagedObject subclass that introduces an ID scope (like Elements or Components or Templates do). The mixin implements a registry of objects, keyed by their ID. It allows to get a map object with all currently known instances, to retrieving a single instance by its key, to execute some callback on each instance, to filter all instances by a given condition (predicate function) and to get the number of instances. The change applies the mixin to Component and Element and removes the previous implementation from the Core. Existing Core APIs redirect to the functions provided by the mixin. Existing workarounds to access the old internal registries in the Core are replaced by the new APIs. Change-Id: Ie8fab2fdf591a0cc6a9b8146ced5f18a6c7e672b JIRA: CPOUIFPHOENIXCORE-2725
- Loading branch information
1 parent
2328598
commit 54df6ca
Showing
26 changed files
with
873 additions
and
447 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
185 changes: 185 additions & 0 deletions
185
src/sap.ui.core/src/sap/ui/base/ManagedObjectRegistry.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/*! | ||
* ${copyright} | ||
*/ | ||
|
||
sap.ui.define(["sap/ui/base/ManagedObject", "sap/base/Log", "sap/base/assert"], | ||
function(ManagedObject, Log, assert) { | ||
"use strict"; | ||
|
||
function apply(FNClass, oOptions) { | ||
|
||
if ( typeof FNClass !== 'function' || !(FNClass.prototype instanceof ManagedObject) ) { | ||
throw new TypeError("ManagedObjectRegistry mixin can only be applied to subclasses of sap.ui.base.ManagedObject"); | ||
} | ||
|
||
oOptions = oOptions || {}; | ||
|
||
var fnOnDuplicate = oOptions.onDuplicate || function(sId, oldInstance, newInstance) { | ||
var sStereotype = FNClass.getMetadata().getStereotype(); | ||
Log.error("adding object \"" + sStereotype + "\" with duplicate id '" + sId + "'"); | ||
throw new Error("Error: adding object \"" + sStereotype + "\" with duplicate id '" + sId + "'"); | ||
}; | ||
|
||
var fnOnDeregister = oOptions.onDeregister || null; | ||
|
||
/** | ||
* Map (object) of objects keyed by their ID. | ||
* @private | ||
*/ | ||
var mInstances = Object.create(null); | ||
|
||
/** | ||
* Number of objects in <code>mInstances</code>. | ||
* @private | ||
*/ | ||
var mInstancesCount = 0; | ||
|
||
FNClass.prototype.register = function register() { | ||
var sId = this.getId(), | ||
old = mInstances[sId]; | ||
|
||
if ( old && old !== this ) { | ||
fnOnDuplicate(sId, old, this); | ||
// executes only if duplicate check succeeds | ||
mInstancesCount--; | ||
} | ||
|
||
mInstances[sId] = this; | ||
mInstancesCount++; | ||
}; | ||
|
||
FNClass.prototype.deregister = function deregister() { | ||
if ( mInstances[this.sId] ) { | ||
if ( fnOnDeregister ) { | ||
fnOnDeregister(this.sId); | ||
} | ||
delete mInstances[this.sId]; | ||
mInstancesCount--; | ||
} | ||
}; | ||
|
||
FNClass["registry"] = Object.freeze({ | ||
|
||
/* | ||
* Returns the number of existing objects. | ||
* | ||
* @returns {int} Number of currently existing objects. | ||
*/ | ||
get size() { | ||
return mInstancesCount; | ||
}, | ||
|
||
/* | ||
* Return an object with all registered object instances, keyed by their ID. | ||
* | ||
* Each call creates a new snapshot object. Depending on the size of the UI, | ||
* this operation therefore might be expensive. Consider to use the <code>forEach</code> | ||
* or <code>filter</code> method instead of executing the same operations on the returned | ||
* object. | ||
* | ||
* <b>Note</b>: The returned object is created by a call to <code>Object.create(null)</code>, | ||
* so it doesn't have a prototype and therefore no <code>toString</code> method. | ||
* | ||
* @returns {object} Object with all elements, keyed by their ID | ||
*/ | ||
all: function() { | ||
var mResults = Object.create(null); | ||
return Object.assign(mResults, mInstances); | ||
}, | ||
|
||
/* | ||
* Retrieves an object by its ID. | ||
* | ||
* @returns {sap.ui.core.Element} Object with the given ID or <code>undefined</code> | ||
*/ | ||
get: function(id) { | ||
assert(id == null || typeof id === "string", "id must be a string when defined"); | ||
// allow null, as this occurs frequently and it is easier to check whether there is a control in the end than | ||
// first checking whether there is an ID and then checking for a control | ||
return id == null ? undefined : mInstances[id]; | ||
}, | ||
|
||
/* | ||
* Calls the given <code>callback</code> for each object. | ||
* | ||
* If objects are created or destroyed during the <code>forEach</code> loop, then the behavior | ||
* is undefined. Newly added elements might or might not be visited. If an element is destroyed | ||
* during the loop and was not visited yet, it won't be visited. | ||
* | ||
* <code>function callback(element, id)</code> | ||
* | ||
* If a <code>thisArg</code> is given, it will be provided as <code>this</code> context when calling | ||
* <code>callback</code>. The <code>this</code> value that the implementation of <code>callback</code> | ||
* sees, depends on the usual resolution mechanism. E.g. when <code>callback</code> was bound to some | ||
* context object, that object wins over the given <code>thisArg</code>. | ||
* | ||
* @param {function(sap.ui.core.Element,sap.ui.core.ID)} callback | ||
* Function to call for each element | ||
* @param {Object} [thisArg=undefined] | ||
* Context object to provide as <code>this</code> in each call of <code>callback</code> | ||
* @throws {TypeError} If <code>callback</code> is not a function | ||
*/ | ||
forEach: function(callback, thisArg) { | ||
if (typeof callback !== "function") { | ||
throw new TypeError(callback + " is not a function"); | ||
} | ||
if ( thisArg != null ) { | ||
callback = callback.bind(thisArg); | ||
} | ||
for ( var id in mInstances ) { | ||
callback(mInstances[id], id); | ||
} | ||
}, | ||
|
||
/* | ||
* Collects all elements for which the given <code>callback</code> returns a value that coerces | ||
* to <code>true</code>. | ||
* | ||
* If elements are created or destroyed within the <code>callback</code>, then the behavior is | ||
* undefined. Newly added objects might or might not be visited. If an element is destroyed | ||
* during the filtering and was not visited yet, it won't be visited. | ||
* | ||
* If a <code>thisArg</code> is given, it will be provided as <code>this</code> context when calling | ||
* <code>callback</code>. The <code>this</code> value that the implementation of <code>callback</code> | ||
* sees, depends on the usual resolution mechanism. E.g. when <code>callback</code> was bound to some | ||
* context object, that object wins over the given <code>thisArg</code>. | ||
* | ||
* This function returns an array with all elements matching the given predicate. The order of the | ||
* elements in the array is undefined and might change between calls (over time and across different | ||
* versions of UI5). | ||
* | ||
* @param {function(sap.ui.core.Element,sap.ui.core.ID)} callback | ||
* predicate against which each element is tested | ||
* @param {Object} thisArg | ||
* context object to provide as <code>this</code> in each call of <code>callback</code> | ||
* @returns {sap.ui.core.Element[]} | ||
* Array of elements matching the predicate; order is undefined and might change in newer versions of UI5 | ||
* @throws {TypeError} If <code>callback</code> is not a function | ||
*/ | ||
filter: function(callback, thisArg) { | ||
if (typeof callback !== "function") { | ||
throw new TypeError(callback + " is not a function"); | ||
} | ||
if ( thisArg != null ) { | ||
callback = callback.bind(thisArg); | ||
} | ||
var result = [], | ||
id; | ||
for ( id in mInstances ) { | ||
if ( callback(mInstances[id], id) ) { | ||
result.push(mInstances[id]); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
}); | ||
|
||
} | ||
|
||
return { | ||
apply : apply | ||
}; | ||
|
||
}); |
Oops, something went wrong.