Namespace.js
6.3 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
* Constructs a new Namespace.
* @exports ProtoBuf.Reflect.Namespace
* @param {!ProtoBuf.Builder} builder Builder reference
* @param {?ProtoBuf.Reflect.Namespace} parent Namespace parent
* @param {string} name Namespace name
* @param {Object.<string,*>=} options Namespace options
* @param {string?} syntax The syntax level of this definition (e.g., proto3)
* @constructor
* @extends ProtoBuf.Reflect.T
*/
var Namespace = function(builder, parent, name, options, syntax) {
T.call(this, builder, parent, name);
/**
* @override
*/
this.className = "Namespace";
/**
* Children inside the namespace.
* @type {!Array.<ProtoBuf.Reflect.T>}
*/
this.children = [];
/**
* Options.
* @type {!Object.<string, *>}
*/
this.options = options || {};
/**
* Syntax level (e.g., proto2 or proto3).
* @type {!string}
*/
this.syntax = syntax || "proto2";
};
/**
* @alias ProtoBuf.Reflect.Namespace.prototype
* @inner
*/
var NamespacePrototype = Namespace.prototype = Object.create(T.prototype);
/**
* Returns an array of the namespace's children.
* @param {ProtoBuf.Reflect.T=} type Filter type (returns instances of this type only). Defaults to null (all children).
* @return {Array.<ProtoBuf.Reflect.T>}
* @expose
*/
NamespacePrototype.getChildren = function(type) {
type = type || null;
if (type == null)
return this.children.slice();
var children = [];
for (var i=0, k=this.children.length; i<k; ++i)
if (this.children[i] instanceof type)
children.push(this.children[i]);
return children;
};
/**
* Adds a child to the namespace.
* @param {ProtoBuf.Reflect.T} child Child
* @throws {Error} If the child cannot be added (duplicate)
* @expose
*/
NamespacePrototype.addChild = function(child) {
var other;
if (other = this.getChild(child.name)) {
// Try to revert camelcase transformation on collision
if (other instanceof Message.Field && other.name !== other.originalName && this.getChild(other.originalName) === null)
other.name = other.originalName; // Revert previous first (effectively keeps both originals)
else if (child instanceof Message.Field && child.name !== child.originalName && this.getChild(child.originalName) === null)
child.name = child.originalName;
else
throw Error("Duplicate name in namespace "+this.toString(true)+": "+child.name);
}
this.children.push(child);
};
/**
* Gets a child by its name or id.
* @param {string|number} nameOrId Child name or id
* @return {?ProtoBuf.Reflect.T} The child or null if not found
* @expose
*/
NamespacePrototype.getChild = function(nameOrId) {
var key = typeof nameOrId === 'number' ? 'id' : 'name';
for (var i=0, k=this.children.length; i<k; ++i)
if (this.children[i][key] === nameOrId)
return this.children[i];
return null;
};
/**
* Resolves a reflect object inside of this namespace.
* @param {string|!Array.<string>} qn Qualified name to resolve
* @param {boolean=} excludeNonNamespace Excludes non-namespace types, defaults to `false`
* @return {?ProtoBuf.Reflect.Namespace} The resolved type or null if not found
* @expose
*/
NamespacePrototype.resolve = function(qn, excludeNonNamespace) {
var part = typeof qn === 'string' ? qn.split(".") : qn,
ptr = this,
i = 0;
if (part[i] === "") { // Fully qualified name, e.g. ".My.Message'
while (ptr.parent !== null)
ptr = ptr.parent;
i++;
}
var child;
do {
do {
if (!(ptr instanceof Reflect.Namespace)) {
ptr = null;
break;
}
child = ptr.getChild(part[i]);
if (!child || !(child instanceof Reflect.T) || (excludeNonNamespace && !(child instanceof Reflect.Namespace))) {
ptr = null;
break;
}
ptr = child; i++;
} while (i < part.length);
if (ptr != null)
break; // Found
// Else search the parent
if (this.parent !== null)
return this.parent.resolve(qn, excludeNonNamespace);
} while (ptr != null);
return ptr;
};
/**
* Determines the shortest qualified name of the specified type, if any, relative to this namespace.
* @param {!ProtoBuf.Reflect.T} t Reflection type
* @returns {string} The shortest qualified name or, if there is none, the fqn
* @expose
*/
NamespacePrototype.qn = function(t) {
var part = [], ptr = t;
do {
part.unshift(ptr.name);
ptr = ptr.parent;
} while (ptr !== null);
for (var len=1; len <= part.length; len++) {
var qn = part.slice(part.length-len);
if (t === this.resolve(qn, t instanceof Reflect.Namespace))
return qn.join(".");
}
return t.fqn();
};
/**
* Builds the namespace and returns the runtime counterpart.
* @return {Object.<string,Function|Object>} Runtime namespace
* @expose
*/
NamespacePrototype.build = function() {
/** @dict */
var ns = {};
var children = this.children;
for (var i=0, k=children.length, child; i<k; ++i) {
child = children[i];
if (child instanceof Namespace)
ns[child.name] = child.build();
}
if (Object.defineProperty)
Object.defineProperty(ns, "$options", { "value": this.buildOpt() });
return ns;
};
/**
* Builds the namespace's '$options' property.
* @return {Object.<string,*>}
*/
NamespacePrototype.buildOpt = function() {
var opt = {},
keys = Object.keys(this.options);
for (var i=0, k=keys.length; i<k; ++i) {
var key = keys[i],
val = this.options[keys[i]];
// TODO: Options are not resolved, yet.
// if (val instanceof Namespace) {
// opt[key] = val.build();
// } else {
opt[key] = val;
// }
}
return opt;
};
/**
* Gets the value assigned to the option with the specified name.
* @param {string=} name Returns the option value if specified, otherwise all options are returned.
* @return {*|Object.<string,*>}null} Option value or NULL if there is no such option
*/
NamespacePrototype.getOption = function(name) {
if (typeof name === 'undefined')
return this.options;
return typeof this.options[name] !== 'undefined' ? this.options[name] : null;
};