"use strict";
module.exports = Root;
var Namespace = require("./namespace");
((Root.prototype = Object.create(Namespace.prototype)).constructor = Root).className = "Root";
var Field = require("./field"),
Enum = require("./enum"),
OneOf = require("./oneof"),
util = require("./util");
var Type, parse, common;
function Root(options) {
Namespace.call(this, "", options);
this.deferred = [];
this.files = [];
this._edition = "proto2";
this._fullyQualifiedObjects = {};
}
Root.fromJSON = function fromJSON(json, root) {
if (!root)
root = new Root();
if (json.options)
root.setOptions(json.options);
return root.addJSON(json.nested).resolveAll();
};
Root.prototype.resolvePath = util.path.resolve;
Root.prototype.fetch = util.fetch;
function SYNC() {}
Root.prototype.load = function load(filename, options, callback) {
if (typeof options === "function") {
callback = options;
options = undefined;
}
var self = this;
if (!callback) {
return util.asPromise(load, self, filename, options);
}
var sync = callback === SYNC;
function finish(err, root) {
if (!callback) {
return;
}
if (sync) {
throw err;
}
if (root) {
root.resolveAll();
}
var cb = callback;
callback = null;
cb(err, root);
}
function getBundledFileName(filename) {
var idx = filename.lastIndexOf("google/protobuf/");
if (idx > -1) {
var altname = filename.substring(idx);
if (altname in common) return altname;
}
return null;
}
function process(filename, source) {
try {
if (util.isString(source) && source.charAt(0) === "{")
source = JSON.parse(source);
if (!util.isString(source))
self.setOptions(source.options).addJSON(source.nested);
else {
parse.filename = filename;
var parsed = parse(source, self, options),
resolved,
i = 0;
if (parsed.imports)
for (; i < parsed.imports.length; ++i)
if (resolved = getBundledFileName(parsed.imports[i]) || self.resolvePath(filename, parsed.imports[i]))
fetch(resolved);
if (parsed.weakImports)
for (i = 0; i < parsed.weakImports.length; ++i)
if (resolved = getBundledFileName(parsed.weakImports[i]) || self.resolvePath(filename, parsed.weakImports[i]))
fetch(resolved, true);
}
} catch (err) {
finish(err);
}
if (!sync && !queued) {
finish(null, self); }
}
function fetch(filename, weak) {
filename = getBundledFileName(filename) || filename;
if (self.files.indexOf(filename) > -1) {
return;
}
self.files.push(filename);
if (filename in common) {
if (sync) {
process(filename, common[filename]);
} else {
++queued;
setTimeout(function() {
--queued;
process(filename, common[filename]);
});
}
return;
}
if (sync) {
var source;
try {
source = util.fs.readFileSync(filename).toString("utf8");
} catch (err) {
if (!weak)
finish(err);
return;
}
process(filename, source);
} else {
++queued;
self.fetch(filename, function(err, source) {
--queued;
if (!callback) {
return; }
if (err) {
if (!weak)
finish(err);
else if (!queued) finish(null, self);
return;
}
process(filename, source);
});
}
}
var queued = 0;
if (util.isString(filename)) {
filename = [ filename ];
}
for (var i = 0, resolved; i < filename.length; ++i)
if (resolved = self.resolvePath("", filename[i]))
fetch(resolved);
if (sync) {
self.resolveAll();
return self;
}
if (!queued) {
finish(null, self);
}
return self;
};
Root.prototype.loadSync = function loadSync(filename, options) {
if (!util.isNode)
throw Error("not supported");
return this.load(filename, options, SYNC);
};
Root.prototype.resolveAll = function resolveAll() {
if (!this._needsRecursiveResolve) return this;
if (this.deferred.length)
throw Error("unresolvable extensions: " + this.deferred.map(function(field) {
return "'extend " + field.extend + "' in " + field.parent.fullName;
}).join(", "));
return Namespace.prototype.resolveAll.call(this);
};
var exposeRe = /^[A-Z]/;
function tryHandleExtension(root, field) {
var extendedType = field.parent.lookup(field.extend);
if (extendedType) {
var sisterField = new Field(field.fullName, field.id, field.type, field.rule, undefined, field.options);
if (extendedType.get(sisterField.name)) {
return true;
}
sisterField.declaringField = field;
field.extensionField = sisterField;
extendedType.add(sisterField);
return true;
}
return false;
}
Root.prototype._handleAdd = function _handleAdd(object) {
if (object instanceof Field) {
if ( object.extend !== undefined && !object.extensionField)
if (!tryHandleExtension(this, object))
this.deferred.push(object);
} else if (object instanceof Enum) {
if (exposeRe.test(object.name))
object.parent[object.name] = object.values;
} else if (!(object instanceof OneOf)) {
if (object instanceof Type) for (var i = 0; i < this.deferred.length;)
if (tryHandleExtension(this, this.deferred[i]))
this.deferred.splice(i, 1);
else
++i;
for (var j = 0; j < object.nestedArray.length; ++j) this._handleAdd(object._nestedArray[j]);
if (exposeRe.test(object.name))
object.parent[object.name] = object; }
if (object instanceof Type || object instanceof Enum || object instanceof Field) {
this._fullyQualifiedObjects[object.fullName] = object;
}
};
Root.prototype._handleRemove = function _handleRemove(object) {
if (object instanceof Field) {
if ( object.extend !== undefined) {
if ( object.extensionField) { object.extensionField.parent.remove(object.extensionField);
object.extensionField = null;
} else { var index = this.deferred.indexOf(object);
if (index > -1)
this.deferred.splice(index, 1);
}
}
} else if (object instanceof Enum) {
if (exposeRe.test(object.name))
delete object.parent[object.name];
} else if (object instanceof Namespace) {
for (var i = 0; i < object.nestedArray.length; ++i) this._handleRemove(object._nestedArray[i]);
if (exposeRe.test(object.name))
delete object.parent[object.name];
}
delete this._fullyQualifiedObjects[object.fullName];
};
Root._configure = function(Type_, parse_, common_) {
Type = Type_;
parse = parse_;
common = common_;
};