--- /dev/null
+/*\r
+ Copyright 2013 Daniel Wirtz <dcode@dcode.io>\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+ */\r
+var description = "Plain JSON descriptor";\r
+\r
+var ProtoBuf = require(__dirname+"/../../../index.js"),\r
+ util = require("../util.js");\r
+\r
+/**\r
+ * pbjs target: Plain JSON descriptor\r
+ * @exports pbjs/targets/json\r
+ * @function\r
+ * @param {!ProtoBuf.Builder} builder Builder\r
+ * @param {!Object.<string,*>=} options Options\r
+ * @returns {string}\r
+ */\r
+var json = module.exports = function(builder, options) {\r
+ options = options || {};\r
+ builder.resolveAll();\r
+\r
+ // Set the pointer to the lowest common namespace (with options)\r
+ var ptr = builder.ns;\r
+ while (ptr.children.length === 1 && Object.keys(ptr.options).length === 0 && ptr.children[0].className === "Namespace")\r
+ ptr = ptr.children[0];\r
+\r
+ // Start by building the package namespace\r
+ var pkg = ptr.fqn().substring(1),\r
+ out = {\r
+ "package": pkg !== "" ? pkg : null\r
+ };\r
+ buildNamespace(ptr, out);\r
+ return JSON.stringify(out, null, options.min ? 0 : 4);\r
+};\r
+\r
+/**\r
+ * Module description.\r
+ * @type {string}\r
+ */\r
+json.description = description;\r
+\r
+/**\r
+ * Builds all structures in a namespace.\r
+ * @param {!ProtoBuf.Reflect.Namespace} ns Namespace to build\r
+ * @param {!Object.<string,*>} out Extended output object\r
+ */\r
+function buildNamespace(ns, out) {\r
+ var messages, enums, services;\r
+ util.extend(out, {\r
+ "syntax" : ns.syntax || 'proto2',\r
+ "options" : out.options || {},\r
+ "messages" : messages = [],\r
+ "enums" : enums = [],\r
+ "services" : services = []\r
+ });\r
+ if (!(ns instanceof ProtoBuf.Reflect.Message))\r
+ out['isNamespace'] = true;\r
+ util.extend(out["options"], buildOptions(ns.options));\r
+ ns.getChildren(ProtoBuf.Reflect.Enum).forEach(function(enm) {\r
+ enums.push(buildEnum(enm));\r
+ });\r
+ if (enums.length === 0)\r
+ delete out["enums"];\r
+ ns.getChildren(ProtoBuf.Reflect.Message).forEach(function(msg) {\r
+ messages.push(buildMessage(msg));\r
+ });\r
+ ns.getChildren(ProtoBuf.Reflect.Service).forEach(function(svc) {\r
+ services.push(buildService(svc));\r
+ });\r
+ if (services.length === 0)\r
+ delete out["services"];\r
+ Array.prototype.push.apply(messages, buildExtensions(ns));\r
+ ns.getChildren(ProtoBuf.Reflect.Namespace).forEach(function(innerNs) {\r
+ if (innerNs.className !== "Namespace")\r
+ return;\r
+ var emptyMessage = {\r
+ "name": innerNs.name,\r
+ "fields": []\r
+ };\r
+ buildNamespace(innerNs, emptyMessage);\r
+ messages.push(emptyMessage);\r
+ });\r
+ if (messages.length === 0)\r
+ delete out["messages"];\r
+ if (Object.keys(out["options"]).length === 0)\r
+ delete out["options"];\r
+}\r
+\r
+/**\r
+ * Builds extensions declared in the specified namespace.\r
+ * @param {!ProtoBuf.Reflect.Namespace} ns Namespace\r
+ * @returns {!Array.<!*>}\r
+ */\r
+function buildExtensions(ns) {\r
+ var exts = util.groupExtensions(ns);\r
+ if (exts === null)\r
+ return [];\r
+ var messages = [];\r
+ Object.keys(exts).forEach(function(extFqn) {\r
+ var extMsg = ns.resolve(extFqn),\r
+ extFields = exts[extFqn];\r
+ var fields, ext = {\r
+ "ref" : ns.qn(extMsg),\r
+ "fields" : fields = []\r
+ };\r
+ extFields.forEach(function(extField) {\r
+ fields.push(buildMessageField(extField));\r
+ });\r
+ messages.push(ext);\r
+ });\r
+ return messages;\r
+}\r
+\r
+/**\r
+ * Builds block-level options.\r
+ * @param {!Object.<string,*>} options Options\r
+ * @returns {!Object.<string,*>}\r
+ */\r
+function buildOptions(options) {\r
+ Object.keys(options = options || {}).forEach(function(key) {\r
+ var val = options[key];\r
+ switch (typeof val) {\r
+ case 'string':\r
+ case 'number':\r
+ case 'boolean':\r
+ case 'object':\r
+ break;\r
+ default:\r
+ throw Error("Illegal option type: "+typeof(val));\r
+ }\r
+ });\r
+ return options;\r
+}\r
+\r
+/**\r
+ * Builds a message.\r
+ * @param {!ProtoBuf.Reflect.Message} msg Message\r
+ * @returns {!*}\r
+ */\r
+function buildMessage(msg) {\r
+ var fields, oneofs;\r
+ var out = {\r
+ "name" : msg.name,\r
+ "syntax" : msg.syntax || 'proto2',\r
+ "options" : {},\r
+ "fields" : fields = [],\r
+ "oneofs" : oneofs = {}\r
+ };\r
+ msg.getChildren(ProtoBuf.Reflect.Message.Field).forEach(function(fld) {\r
+ if (fld instanceof ProtoBuf.Reflect.Message.ExtensionField)\r
+ return;\r
+ fields.push(buildMessageField(fld));\r
+ });\r
+ msg.getChildren(ProtoBuf.Reflect.Message.OneOf).forEach(function(oneof) {\r
+ oneofs[oneof.name] = buildMessageOneof(oneof);\r
+ });\r
+ if (msg.extensions)\r
+ out["extensions"] = msg.extensions;\r
+ if (Object.keys(oneofs).length === 0)\r
+ delete out["oneofs"];\r
+ buildNamespace(msg, out);\r
+ return out;\r
+}\r
+\r
+/**\r
+ * Builds a message field.\r
+ * @param {!ProtoBuf.Reflect.Message.Field} fld Message field\r
+ * @returns {!*}\r
+ */\r
+function buildMessageField(fld) {\r
+ return {\r
+ "rule" : fld.map ? "map" : (fld.repeated ? "repeated" : (fld.required ? "required" : "optional")),\r
+ "type" : fld.resolvedType ? fld.parent.qn(fld.resolvedType) : fld.type['name'],\r
+ "keytype" : (typeof(fld.keyType) === 'string') ? fld.keyType : (fld.keyType !== null ? fld.keyType.name : undefined),\r
+ "name" : fld instanceof ProtoBuf.Reflect.Message.ExtensionField ? fld.name.substring(fld.name.lastIndexOf(".")+1): fld.name,\r
+ "id" : fld.id,\r
+ "options" : Object.keys(fld.options).length > 0 ? buildOptions(fld.options) : undefined,\r
+ "oneof" : fld.oneof ? fld.oneof.name : undefined\r
+ };\r
+}\r
+\r
+/**\r
+ * Builds a message oneof.\r
+ * @param {!ProtoBuf.Reflect.message.OneOf} oneof Message oneof\r
+ * @returns {!Array.<!*>}\r
+ */\r
+function buildMessageOneof(oneof) {\r
+ var out = [];\r
+ oneof.fields.forEach(function(fld) {\r
+ out.push(fld.id);\r
+ });\r
+ return out;\r
+}\r
+\r
+/**\r
+ * Builds an enum.\r
+ * @param {!ProtoBuf.Reflect.Enum} enm Enum\r
+ * @returns {!*}\r
+ */\r
+function buildEnum(enm) {\r
+ var values;\r
+ var out = {\r
+ "name" : enm.name,\r
+ "syntax" : enm.syntax || 'proto2',\r
+ "values" : values = []\r
+ };\r
+ enm.getChildren(ProtoBuf.Reflect.Enum.Value).forEach(function(val) {\r
+ values.push(buildEnumValue(val));\r
+ });\r
+ if (Object.keys(enm.options).length > 0)\r
+ out["options"] = buildOptions(enm.options);\r
+ return out;\r
+}\r
+\r
+/**\r
+ * Builds an enum value.\r
+ * @param {!ProtoBuf.Reflect.Enum.Value} val Enum value\r
+ * @returns {!*}\r
+ */\r
+function buildEnumValue(val) {\r
+ return {\r
+ "name" : val.name,\r
+ "id" : val.id\r
+ };\r
+}\r
+\r
+/**\r
+ * Builds a service.\r
+ * @param {!ProtoBuf.Reflect.Service} svc Service\r
+ * @returns {!*}\r
+ */\r
+function buildService(svc) {\r
+ var rpc;\r
+ var out = {\r
+ "name": svc.name,\r
+ "options": buildOptions(svc.options),\r
+ "rpc": rpc = {}\r
+ };\r
+ svc.getChildren(ProtoBuf.Reflect.Service.RPCMethod).forEach(function(mtd) {\r
+ rpc[mtd.name] = {\r
+ "request": svc.qn(mtd.resolvedRequestType),\r
+ "request_stream": mtd.requestStream,\r
+ "response": svc.qn(mtd.resolvedResponseType),\r
+ "response_stream": mtd.responseStream,\r
+ "options": buildOptions(mtd.options)\r
+ };\r
+ });\r
+ return out;\r
+}\r