diff --git a/lib/Project.js b/lib/Project.js index 9786cc4..0519d70 100644 --- a/lib/Project.js +++ b/lib/Project.js @@ -203,28 +203,45 @@ Project.prototype._processProto = function (fileName) { proto.addImport(this._processProto(importName)) }, this) - this._resolveTypes(proto) + this._discoverTypes(proto, proto.getPackage()) + this._resolveTypes(proto, proto.getPackage()) } return proto } /** - * Resolve all protobuf types into the FieldDescriptors that name them. - * @param {ProtoDescriptor} proto + * Index all protobuf types + * @param {MessageDescriptor|ProtoDescriptor} proto + * @param {string} package * @private */ -Project.prototype._resolveTypes = function (proto) { +Project.prototype._discoverTypes = function (proto, package) { proto.getMessages().forEach(function (message) { - var messageName = proto.getPackage() + '.' + message.getName() + message.setPackage(package) + + var messageName = helper.joinPackage(package, message.getName()) this._typesByName[messageName] = message + + this._discoverTypes(message, messageName) }, this) proto.getEnums().forEach(function (e) { - var eName = proto.getPackage() + '.' + e.getName() + e.setPackage(package) + + var eName = helper.joinPackage(package, e.getName()) this._typesByName[eName] = e }, this) +} + +/** + * Resolve all protobuf types into the FieldDescriptors that name them. + * @param {MessageDescriptor|ProtoDescriptor} proto + * @param {string} package + * @private + */ +Project.prototype._resolveTypes = function (proto, package) { proto.getMessages().forEach(function (message) { message.getFields().forEach(function (field) { if (field.isNativeType()) return @@ -236,20 +253,23 @@ Project.prototype._resolveTypes = function (proto) { return } - if (typeName.indexOf('.') == -1) { - typeName = proto.getPackage() + '.' + typeName - } - - var type = this._typesByName[typeName] - if (type) { - field.setTypeDescriptor(type) - return + var packageStack = package.split('.') + for (var i = 0; i <= packageStack.length; i++) { + var scope = packageStack.slice(0, i).join('.') + var globalTypeName = helper.joinPackage(scope, typeName) + var type = this._typesByName[globalTypeName] + if (type) { + field.setTypeDescriptor(type) + return + } } throw new Error('Could not resolve type of field ' + field.getName() + ' on message ' + message.getName() + ' : ' + field.getRawType()) }, this) + + this._resolveTypes(message, helper.joinPackage(package, message.getName())) }, this) } diff --git a/lib/descriptors/EnumDescriptor.js b/lib/descriptors/EnumDescriptor.js index bb9b0b6..3dc9174 100644 --- a/lib/descriptors/EnumDescriptor.js +++ b/lib/descriptors/EnumDescriptor.js @@ -18,6 +18,7 @@ function EnumDescriptor(name) { this._name = name this._numbers = {} this._names = {} + this._package = '' } util.inherits(EnumDescriptor, Descriptor) module.exports = EnumDescriptor @@ -34,6 +35,7 @@ EnumDescriptor.prototype.toTemplateObject = function () { numbers.sort() return { name: this._name, + fullName: helper.joinPackage(this._package, this._name), values: numbers.map(function (n) { return this.getValueForNumber(n) }, this), @@ -41,6 +43,12 @@ EnumDescriptor.prototype.toTemplateObject = function () { } } + +EnumDescriptor.prototype.setPackage = function (package) { + this._package = package +} + + EnumDescriptor.prototype.getName = function () { return this._name } diff --git a/lib/descriptors/MessageDescriptor.js b/lib/descriptors/MessageDescriptor.js index df62a65..e0d28ab 100644 --- a/lib/descriptors/MessageDescriptor.js +++ b/lib/descriptors/MessageDescriptor.js @@ -22,6 +22,7 @@ function MessageDescriptor(name) { this._enums = {} this._fields = {} this._fieldNames = {} + this._package = '' } util.inherits(MessageDescriptor, Descriptor) module.exports = MessageDescriptor @@ -40,6 +41,7 @@ MessageDescriptor.prototype.inspect = function () { MessageDescriptor.prototype.toTemplateObject = function (opt_skipFields) { var obj = { name: this._name, + fullName: helper.joinPackage(this._package, this._name), camelName: this.getCamelName(), options: this._options } @@ -54,6 +56,11 @@ MessageDescriptor.prototype.toTemplateObject = function (opt_skipFields) { } +MessageDescriptor.prototype.setPackage = function (package) { + this._package = package +} + + MessageDescriptor.prototype.addOption = function (name, value) { this._options[name] = value return this @@ -158,6 +165,11 @@ MessageDescriptor.prototype.addEnum = function (enumeration) { } +MessageDescriptor.prototype.getEnums = function () { + return helper.values(this._enums) +} + + MessageDescriptor.prototype.getEnum = function (name) { return this._enums[name] } diff --git a/lib/helper.js b/lib/helper.js index 7d66467..151eff1 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -122,3 +122,13 @@ exports.mkdir = function(name) { mkdirp(dir, deferred.makeNodeResolver()) return deferred.promise } + +/** + * Join a package to a name, where the package may be the default package. + * @param {string} package + * @param {string} name + * @return {string} + */ +exports.joinPackage = function (package, name) { + return package ? (package + '.' + name) : name +} diff --git a/tests/project_test.js b/tests/project_test.js index 75b6e01..42292b5 100644 --- a/tests/project_test.js +++ b/tests/project_test.js @@ -35,7 +35,7 @@ builder.add(function testGetProtos(test) { {name: 'HOME', titleName: 'Home', number: 1}, {name: 'WORK', titleName: 'Work', number: 2}, {name: 'WORK_FAX', titleName: 'WorkFax', number: 3}], - isEnum: true}, + isEnum: true, fullName: 'examples.Person.PhoneType'}, enums[0]) test.done() @@ -124,6 +124,7 @@ builder.add(function testRemoveField(test) { .addProto('protos/common.proto') var color = project.getProtos('protos/common.proto')[0].getMessage('Color') + console.log('COLOR', color.toTemplateObject) test.equal(3, color.toTemplateObject().fields.length) color.removeFieldByName('red') @@ -141,6 +142,19 @@ builder.add(function testTypeResolution(test) { test.done() }) +builder.add(function testTypeResolutionInner(test) { + var project = new Project(baseDir) + .addProto('protos/inner.proto') + + var tortilla = project.getProtos('protos/inner.proto')[0].getMessage('Tortilla') + var tortillaJson = tortilla.toTemplateObject() + test.equal('burrito.Tortilla.Filling', tortillaJson.fields[0].typeDescriptor.fullName) + test.equal('burrito.Tortilla.Filling', tortillaJson.fields[1].typeDescriptor.fullName) + test.equal('burrito.Tortilla.Guac', tortillaJson.fields[2].typeDescriptor.fullName) + test.equal('burrito.Tortilla.Guac', tortillaJson.fields[3].typeDescriptor.fullName) + test.done() +}) + builder.add(function testTypeResolutionLoop(test) { var project = new Project(baseDir) .addProto('protos/loop.proto') diff --git a/tests/protos/inner.proto b/tests/protos/inner.proto new file mode 100644 index 0000000..b532ec1 --- /dev/null +++ b/tests/protos/inner.proto @@ -0,0 +1,21 @@ +package burrito; + +message Tortilla { + enum Filling { + CHICKEN = 1; + BEEF = 2; + TOFU = 3; + } + + message Guac { + repeated Avocado avacados = 1; + } + + optional Filling filling1 = 2; + optional burrito.Tortilla.Filling filling2 = 3; + optional Guac guac1 = 4; + optional burrito.Tortilla.Guac guac2 = 5; +} + +message Avocado { +} \ No newline at end of file