-
Notifications
You must be signed in to change notification settings - Fork 351
/
registry.js
148 lines (134 loc) · 4.84 KB
/
registry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
define(function(require) {
'use strict';
const _ = require('underscore');
const Backbone = require('backbone');
const RegistryEntry = require('oroui/js/app/services/registry/registry-entry');
/** @typedef {(BaseModel|BaseCollection|BaseView|BaseComponent|BaseClass)} RegistryApplicant */
function Registry() {
this._entries = {};
}
Registry.prototype = _.extend(Object.create(Backbone.Events), {
_entries: null,
/**
* Defines custom access methods to registry entries
*
* @param {Object.<string, function>} methods
* @mixes methods
*/
declareAccessMethods: function(methods) {
_.each(methods, function(method, name) {
if (name in this) {
throw new Error('The registry already has `' + name + '` property');
}
this[name] = method;
}, this);
},
/**
* Adds applicant relation to registry for the instance
*
* @param {{globalId: string}} instance
* @param {RegistryApplicant} applicant
*/
retain: function(instance, applicant) {
const obj = this.fetch(instance.globalId, applicant);
if (!obj) {
this.put(instance, applicant);
}
},
/**
* Removes applicant relation from registry entry of instance
*
* @param {{globalId: string}} instance
* @param {RegistryApplicant} applicant
*/
relieve: function(instance, applicant) {
const entry = this._entries[instance.globalId];
if (entry) {
entry.removeApplicant(applicant);
}
},
/**
* Fetches instance from registry by globalId
*
* @param {string} globalId
* @param {RegistryApplicant} applicant
* @return {Object|null}
*/
fetch: function(globalId, applicant) {
const entry = this._entries[globalId];
if (entry) {
entry.addApplicant(applicant);
}
return entry ? entry.instance : null;
},
/**
* Puts the instance with related applicant to the registry
*
* @param {{globalId: string}} instance
* @param {RegistryApplicant} applicant
* @throws {Error} invalid instance or instance already exists in registry
*/
put: function(instance, applicant) {
const globalId = instance.globalId;
if (!globalId) {
throw new Error('globalId "' + globalId + '" of instance have not to be empty');
}
if (this._entries[globalId]) {
throw new Error('Instance with globalId "' + globalId + '" is already registered');
}
const entry = this._entries[globalId] = new RegistryEntry(instance);
entry.addApplicant(applicant);
this.listenToOnce(instance, 'dispose', function() {
this._removeEntry(entry);
});
this.listenTo(entry, 'removeApplicant', function(entry) {
const instance = entry.instance;
if (!entry.applicants.length || !this._hasExternalRequester(entry)) {
this._removeEntry(entry);
instance.dispose();
}
});
},
/**
* Check if there's any applicant that is not an entry.instance (means external applicant)
*
* @param {RegistryEntry} entry
* @return {boolean}
*/
_hasExternalRequester: function(entry) {
const queue = [entry];
const checked = [];
let relatedEntry;
let currentEntry;
do {
currentEntry = queue.pop();
checked.push(currentEntry);
for (let i = 0; currentEntry.applicants.length > i; i++) {
relatedEntry = this._entries[currentEntry.applicants[i].globalId];
if (!relatedEntry) {
return true;
} else if (checked.indexOf(relatedEntry) === -1 && queue.indexOf(relatedEntry) === -1) {
queue.push(relatedEntry);
}
}
} while (queue.length > 0);
return false;
},
/**
* Removes an entry from registry
*
* @param {RegistryEntry} entry
* @protected
*/
_removeEntry: function(entry) {
const globalId = _.findKey(this._entries, entry);
this.stopListening(entry);
entry.dispose();
delete this._entries[globalId];
}
});
/**
* @export oroui/js/app/services/registry/registry
*/
return Registry;
});