blob: fa67d86988c3ef4e99d3e62576086202aad933c4 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
42 "log"
43 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070044 "path"
David Symonds79eae332010-10-16 11:33:20 +110045 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070046 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070047
48 "goprotobuf.googlecode.com/hg/proto"
David Symonds832b2432010-11-11 10:55:27 +110049 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070050 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
51)
52
53// A Plugin provides functionality to add to the output during Go code generation,
54// such as to produce RPC stubs.
55type Plugin interface {
56 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070057 Name() string
58 // Init is called once after data structures are built but before
59 // code generation begins.
60 Init(g *Generator)
61 // Generate produces the code generated by the plugin for this file,
62 // except for the imports, by calling the generator's methods P, In, and Out.
63 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070064 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070065 // It is called after Generate.
66 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067}
68
69var plugins []Plugin
70
71// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
72// It is typically called during initialization.
73func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110074 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070075}
76
77// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
78// a pointer to the FileDescriptorProto that represents it. These types achieve that
79// wrapping by placing each Proto inside a struct with the pointer to its File. The
80// structs have the same names as their contents, with "Proto" removed.
81// FileDescriptor is used to store the things that it points to.
82
83// The file and package name method are common to messages and enums.
84type common struct {
85 File *descriptor.FileDescriptorProto // File this object comes from.
86}
87
88// PackageName is name in the package clause in the generated file.
89func (c *common) PackageName() string { return uniquePackageOf(c.File) }
90
91// Descriptor represents a protocol buffer message.
92type Descriptor struct {
93 common
94 *descriptor.DescriptorProto
95 parent *Descriptor // The containing message, if any.
96 nested []*Descriptor // Inner messages, if any.
97 ext []*ExtensionDescriptor // Extensions, if any.
98 typename []string // Cached typename vector.
99}
100
101// TypeName returns the elements of the dotted type name.
102// The package name is not part of this name.
103func (d *Descriptor) TypeName() []string {
104 if d.typename != nil {
105 return d.typename
106 }
107 n := 0
108 for parent := d; parent != nil; parent = parent.parent {
109 n++
110 }
111 s := make([]string, n, n)
112 for parent := d; parent != nil; parent = parent.parent {
113 n--
114 s[n] = proto.GetString(parent.Name)
115 }
116 d.typename = s
117 return s
118}
119
120// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
121// Otherwise it will be the descriptor of the message in which it is defined.
122type EnumDescriptor struct {
123 common
124 *descriptor.EnumDescriptorProto
125 parent *Descriptor // The containing message, if any.
126 typename []string // Cached typename vector.
127}
128
129// TypeName returns the elements of the dotted type name.
130// The package name is not part of this name.
131func (e *EnumDescriptor) TypeName() (s []string) {
132 if e.typename != nil {
133 return e.typename
134 }
135 name := proto.GetString(e.Name)
136 if e.parent == nil {
137 s = make([]string, 1)
138 } else {
139 pname := e.parent.TypeName()
140 s = make([]string, len(pname)+1)
141 copy(s, pname)
142 }
143 s[len(s)-1] = name
144 e.typename = s
145 return s
146}
147
148// Everything but the last element of the full type name, CamelCased.
149// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
150func (e *EnumDescriptor) prefix() string {
151 typeName := e.TypeName()
152 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
153 if e.parent == nil {
154 // If the enum is not part of a message, the prefix is just the type name.
155 ccPrefix = CamelCase(*e.Name) + "_"
156 }
157 return ccPrefix
158}
159
160// The integer value of the named constant in this enumerated type.
161func (e *EnumDescriptor) integerValueAsString(name string) string {
162 for _, c := range e.Value {
163 if proto.GetString(c.Name) == name {
164 return fmt.Sprint(proto.GetInt32(c.Number))
165 }
166 }
167 log.Exit("cannot find value for enum constant")
168 return ""
169}
170
171// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
172// Otherwise it will be the descriptor of the message in which it is defined.
173type ExtensionDescriptor struct {
174 common
175 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700176 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700177}
178
179// TypeName returns the elements of the dotted type name.
180// The package name is not part of this name.
181func (e *ExtensionDescriptor) TypeName() (s []string) {
182 name := proto.GetString(e.Name)
183 if e.parent == nil {
184 // top-level extension
185 s = make([]string, 1)
186 } else {
187 pname := e.parent.TypeName()
188 s = make([]string, len(pname)+1)
189 copy(s, pname)
190 }
191 s[len(s)-1] = name
192 return s
193}
194
195// FileDescriptor describes an protocol buffer descriptor file (.proto).
196// It includes slices of all the messages and enums defined within it.
197// Those slices are constructed by WrapTypes.
198type FileDescriptor struct {
199 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700200 desc []*Descriptor // All the messages defined in this file.
201 enum []*EnumDescriptor // All the enums defined in this file.
202 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700203}
204
205// PackageName is the package name we'll use in the generated code to refer to this file.
206func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
207
208// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700209// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700210func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700211 // Does the file have a package clause?
212 pkg := proto.GetString(d.Package)
213 if pkg != "" {
214 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700215 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700216 // Use the file base name.
217 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700218}
219
220// Object is an interface abstracting the abilities shared by enums and messages.
221type Object interface {
222 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
223 TypeName() []string
224}
225
226// Each package name we generate must be unique. The package we're generating
227// gets its own name but every other package must have a unqiue name that does
228// not conflict in the code we generate. These names are chosen globally (although
229// they don't have to be, it simplifies things to do them globally).
230func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
231 s, ok := uniquePackageName[fd]
232 if !ok {
233 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
234 }
235 return s
236}
237
238// Generator is the type whose methods generate the output, stored in the associated response structure.
239type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000240 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700241
242 Request *plugin.CodeGeneratorRequest // The input.
243 Response *plugin.CodeGeneratorResponse // The output.
244
Rob Pikec9e7d972010-06-10 10:30:22 -0700245 Param map[string]string // Command-line parameters.
246 ImportPrefix string // String to prefix to imported package file names.
247 ImportMap map[string]string // Mapping from import name to generated name
248
249 ProtoPkg string // The name under which we import the library's package proto.
250
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700251 packageName string // What we're calling ourselves.
252 allFiles []*FileDescriptor // All files in the tree
253 genFiles []*FileDescriptor // Those files we will generate output for.
254 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000255 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700256 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
257 indent string
258}
259
260// New creates a new generator and allocates the request and response protobufs.
261func New() *Generator {
262 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000263 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100264 g.Request = new(plugin.CodeGeneratorRequest)
265 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700266 return g
267}
268
269// Error reports a problem, including an os.Error, and exits the program.
270func (g *Generator) Error(err os.Error, msgs ...string) {
271 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700272 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700273 g.Response.Error = proto.String(s)
274 os.Exit(1)
275}
276
277// Fail reports a problem and exits the program.
278func (g *Generator) Fail(msgs ...string) {
279 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700280 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700281 g.Response.Error = proto.String(s)
282 os.Exit(1)
283}
284
Rob Pikec9e7d972010-06-10 10:30:22 -0700285// CommandLineParameters breaks the comma-separated list of key=value pairs
286// in the parameter (a member of the request protobuf) into a key/value map.
287// It then sets file name mappings defined by those entries.
288func (g *Generator) CommandLineParameters(parameter string) {
289 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700290 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700291 if i := strings.Index(p, "="); i < 0 {
292 g.Param[p] = ""
293 } else {
294 g.Param[p[0:i]] = p[i+1:]
295 }
296 }
297
298 g.ImportMap = make(map[string]string)
299 for k, v := range g.Param {
300 if k == "import_prefix" {
301 g.ImportPrefix = v
302 } else if len(k) > 0 && k[0] == 'M' {
303 g.ImportMap[k[1:]] = v
304 }
305 }
306}
307
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700308// DefaultPackageName returns the package name printed for the object.
309// If its file is in a different package, it returns the package name we're using for this file, plus ".".
310// Otherwise it returns the empty string.
311func (g *Generator) DefaultPackageName(obj Object) string {
312 pkg := obj.PackageName()
313 if pkg == g.packageName {
314 return ""
315 }
316 return pkg + "."
317}
318
319// For each input file, the unique package name to use, underscored.
320var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700321// Package names already registered. Key is the name from the .proto file;
322// value is the name that appears in the generated code.
323var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700324
Rob Pikec9e7d972010-06-10 10:30:22 -0700325// Create and remember a guaranteed unique package name for this file descriptor.
326// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
327// has no file descriptor.
328func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds79eae332010-10-16 11:33:20 +1100329 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700330 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100331 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700332 }
333 // Install it.
334 pkgNamesInUse[pkg] = true
335 pkg = strings.Map(DotToUnderscore, pkg)
336 if f != nil {
337 uniquePackageName[f.FileDescriptorProto] = pkg
338 }
339 return pkg
340}
341
342// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700343// The package name must agree across all files being generated.
344// It also defines unique package names for all imported files.
345func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700346 // Register the name for this package. It will be the first name
347 // registered so is guaranteed to be unmodified.
348 pkg := g.genFiles[0].originalPackageName()
349 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
350 // Register the proto package name. It might collide with the
351 // name of a package we import.
352 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700353 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700354 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700355 if thisPkg != pkg {
356 g.Fail("inconsistent package names:", thisPkg, pkg)
357 }
358 }
359AllFiles:
360 for _, f := range g.allFiles {
361 for _, genf := range g.genFiles {
362 if f == genf {
363 // In this package already.
364 uniquePackageName[f.FileDescriptorProto] = g.packageName
365 continue AllFiles
366 }
367 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700368 RegisterUniquePackageName(f.originalPackageName(), f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700369 }
370}
371
372// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
373// and FileDescriptorProtos into file-referenced objects within the Generator.
374// It also creates the list of files to generate and so should be called before GenerateAllFiles.
375func (g *Generator) WrapTypes() {
376 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
377 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700378 // We must wrap the descriptors before we wrap the enums
379 descs := wrapDescriptors(f)
380 g.buildNestedDescriptors(descs)
381 enums := wrapEnumDescriptors(f, descs)
382 exts := wrapExtensions(f)
383 g.allFiles[i] = &FileDescriptor{
384 FileDescriptorProto: f,
385 desc: descs,
386 enum: enums,
387 ext: exts,
388 }
389 }
390
391 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
392FindFiles:
393 for i, fileName := range g.Request.FileToGenerate {
394 // Search the list. This algorithm is n^2 but n is tiny.
395 for _, file := range g.allFiles {
396 if fileName == proto.GetString(file.Name) {
397 g.genFiles[i] = file
398 continue FindFiles
399 }
400 }
401 g.Fail("could not find file named", fileName)
402 }
403 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
404}
405
406// Scan the descriptors in this file. For each one, build the slice of nested descriptors
407func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
408 for _, desc := range descs {
409 if len(desc.NestedType) != 0 {
410 desc.nested = make([]*Descriptor, len(desc.NestedType))
411 n := 0
412 for _, nest := range descs {
413 if nest.parent == desc {
414 desc.nested[n] = nest
415 n++
416 }
417 }
418 if n != len(desc.NestedType) {
419 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
420 }
421 }
422 }
423}
424
425// Construct the Descriptor and add it to the slice
426func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
427 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
428
429 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
430 for i, field := range desc.Extension {
431 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
432 }
433
David Symondscc7142e2010-11-06 14:37:15 +1100434 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700435}
436
437// Return a slice of all the Descriptors defined within this file
438func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
439 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
440 for _, desc := range file.MessageType {
441 sl = wrapThisDescriptor(sl, desc, nil, file)
442 }
443 return sl
444}
445
446// Wrap this Descriptor, recursively
447func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
448 sl = addDescriptor(sl, desc, parent, file)
449 me := sl[len(sl)-1]
450 for _, nested := range desc.NestedType {
451 sl = wrapThisDescriptor(sl, nested, me, file)
452 }
453 return sl
454}
455
456// Construct the EnumDescriptor and add it to the slice
457func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symondscc7142e2010-11-06 14:37:15 +1100458 return append(sl, &EnumDescriptor{common{File: file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700459}
460
461// Return a slice of all the EnumDescriptors defined within this file
462func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
463 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000464 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700465 for _, enum := range file.EnumType {
466 sl = addEnumDescriptor(sl, enum, nil, file)
467 }
David Symonds5256cf62010-06-27 10:33:42 +1000468 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700469 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000470 for _, enum := range nested.EnumType {
471 sl = addEnumDescriptor(sl, enum, nested, file)
472 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700473 }
474 return sl
475}
476
477// Return a slice of all the top-level ExtensionDescriptors defined within this file.
478func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
479 sl := make([]*ExtensionDescriptor, len(file.Extension))
480 for i, field := range file.Extension {
481 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
482 }
483 return sl
484}
485
Rob Pikec9e7d972010-06-10 10:30:22 -0700486// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700487// The key names for the map come from the input data, which puts a period at the beginning.
488// It should be called after SetPackageNames and before GenerateAllFiles.
489func (g *Generator) BuildTypeNameMap() {
490 g.typeNameToObject = make(map[string]Object)
491 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700492 // The names in this loop are defined by the proto world, not us, so the
493 // package name may be empty. If so, the dotted package name of X will
494 // be ".X"; otherwise it will be ".pkg.X".
495 dottedPkg := "." + proto.GetString(f.Package)
496 if dottedPkg != "." {
497 dottedPkg += "."
498 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700499 for _, enum := range f.enum {
500 name := dottedPkg + dottedSlice(enum.TypeName())
501 g.typeNameToObject[name] = enum
502 }
503 for _, desc := range f.desc {
504 name := dottedPkg + dottedSlice(desc.TypeName())
505 g.typeNameToObject[name] = desc
506 }
507 }
508}
509
510// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
511// returns the descriptor for the message or enum with that name.
512func (g *Generator) ObjectNamed(typeName string) Object {
513 f, ok := g.typeNameToObject[typeName]
514 if !ok {
515 g.Fail("can't find object with type", typeName)
516 }
517 return f
518}
519
520// P prints the arguments to the generated output. It handles strings and int32s, plus
521// handling indirections because they may be *string, etc.
522func (g *Generator) P(str ...interface{}) {
523 g.WriteString(g.indent)
524 for _, v := range str {
525 switch s := v.(type) {
526 case string:
527 g.WriteString(s)
528 case *string:
529 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700530 case bool:
531 g.WriteString(fmt.Sprintf("%t", s))
532 case *bool:
533 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700534 case *int32:
535 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700536 case float64:
537 g.WriteString(fmt.Sprintf("%g", s))
538 case *float64:
539 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700540 default:
541 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
542 }
543 }
544 g.WriteByte('\n')
545}
546
547// In Indents the output one tab stop.
548func (g *Generator) In() { g.indent += "\t" }
549
550// Out unindents the output one tab stop.
551func (g *Generator) Out() {
552 if len(g.indent) > 0 {
553 g.indent = g.indent[1:]
554 }
555}
556
557// GenerateAllFiles generates the output for all the files we're outputting.
558func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700559 // Initialize the plugins
560 for _, p := range plugins {
561 p.Init(g)
562 }
563 // Generate the output.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700564 for i, file := range g.genFiles {
565 g.Reset()
566 g.generate(file)
David Symondsb0127532010-11-09 11:10:46 +1100567 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700568 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
569 g.Response.File[i].Content = proto.String(g.String())
570 }
571}
572
573// Run all the plugins associated with the file.
574func (g *Generator) runPlugins(file *FileDescriptor) {
575 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700576 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700577 }
578}
579
580
581// FileOf return the FileDescriptor for this FileDescriptorProto.
582func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
583 for _, file := range g.allFiles {
584 if file.FileDescriptorProto == fd {
585 return file
586 }
587 }
588 g.Fail("could not find file in table:", proto.GetString(fd.Name))
589 return nil
590}
591
592// Fill the response protocol buffer with the generated output for all the files we're
593// supposed to generate.
594func (g *Generator) generate(file *FileDescriptor) {
595 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000596 g.usedPackages = make(map[string]bool)
597
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700598 for _, enum := range g.file.enum {
599 g.generateEnum(enum)
600 }
601 for _, desc := range g.file.desc {
602 g.generateMessage(desc)
603 }
604 for _, ext := range g.file.ext {
605 g.generateExtension(ext)
606 }
607 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000608
Rob Pikec9e7d972010-06-10 10:30:22 -0700609 // Run the plugins before the imports so we know which imports are necessary.
610 g.runPlugins(file)
611
David Symondsf90e3382010-05-05 10:53:44 +1000612 // Generate header and imports last, though they appear first in the output.
613 rem := g.Buffer
614 g.Buffer = new(bytes.Buffer)
615 g.generateHeader()
616 g.generateImports()
617 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700618}
619
620// Generate the header, including package definition and imports
621func (g *Generator) generateHeader() {
622 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
623 g.P("// DO NOT EDIT!")
624 g.P()
625 g.P("package ", g.file.PackageName())
626 g.P()
627}
628
629// Generate the header, including package definition and imports
630func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700631 // We almost always need a proto import. Rather than computing when we
632 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100633 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700634 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100635 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100636 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700637 for _, s := range g.file.Dependency {
638 // Need to find the descriptor for this file
639 for _, fd := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700640 // Do not import our own package.
641 if fd.PackageName() == g.packageName {
642 continue
643 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700644 if proto.GetString(fd.Name) == s {
645 filename := goFileName(s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700646 if substitution, ok := g.ImportMap[s]; ok {
647 filename = substitution
648 }
649 filename = g.ImportPrefix + filename
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700650 if strings.HasSuffix(filename, ".go") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700651 filename = filename[0 : len(filename)-3]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700652 }
David Symondsf90e3382010-05-05 10:53:44 +1000653 if _, ok := g.usedPackages[fd.PackageName()]; ok {
654 g.P("import ", fd.PackageName(), " ", Quote(filename))
655 } else {
Rob Pike5194c512010-10-14 13:02:16 -0700656 log.Println("protoc-gen-go: discarding unused import:", filename)
David Symondsf90e3382010-05-05 10:53:44 +1000657 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700658 break
659 }
660 }
661 }
662 g.P()
663 // TODO: may need to worry about uniqueness across plugins
664 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700665 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700666 g.P()
667 }
David Symondscea785b2011-01-07 11:02:30 +1100668 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700669 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100670 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100671 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700672 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700673}
674
675// Generate the enum definitions for this EnumDescriptor.
676func (g *Generator) generateEnum(enum *EnumDescriptor) {
677 // The full type name
678 typeName := enum.TypeName()
679 // The full type name, CamelCased.
680 ccTypeName := CamelCaseSlice(typeName)
681 ccPrefix := enum.prefix()
682 g.P("type ", ccTypeName, " int32")
683 g.P("const (")
684 g.In()
685 for _, e := range enum.Value {
686 g.P(ccPrefix+*e.Name, " = ", e.Number)
687 }
688 g.Out()
689 g.P(")")
690 g.P("var ", ccTypeName, "_name = map[int32] string {")
691 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700692 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700693 for _, e := range enum.Value {
694 duplicate := ""
695 if _, present := generated[*e.Number]; present {
696 duplicate = "// Duplicate value: "
697 }
698 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
699 generated[*e.Number] = true
700 }
701 g.Out()
702 g.P("}")
703 g.P("var ", ccTypeName, "_value = map[string] int32 {")
704 g.In()
705 for _, e := range enum.Value {
706 g.P(Quote(*e.Name), ": ", e.Number, ",")
707 }
708 g.Out()
709 g.P("}")
710 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
711 g.In()
712 g.P("e := ", ccTypeName, "(x)")
713 g.P("return &e")
714 g.Out()
715 g.P("}")
716 g.P()
717}
718
719// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
720// identifies details of the field for the protocol buffer marshaling and unmarshaling
721// code. The fields are:
722// wire encoding
723// protocol tag number
724// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100725// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700726// name= the original declared name
727// enum= the name of the enum type if it is an enum-typed field.
728// def= string representation of the default value, if any.
729// The default value must be in a representation that can be used at run-time
730// to generate the default value. Thus bools become 0 and 1, for instance.
731func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
732 optrepreq := ""
733 switch {
734 case isOptional(field):
735 optrepreq = "opt"
736 case isRequired(field):
737 optrepreq = "req"
738 case isRepeated(field):
739 optrepreq = "rep"
740 }
741 defaultValue := proto.GetString(field.DefaultValue)
742 if defaultValue != "" {
743 switch *field.Type {
744 case descriptor.FieldDescriptorProto_TYPE_BOOL:
745 if defaultValue == "true" {
746 defaultValue = "1"
747 } else {
748 defaultValue = "0"
749 }
750 case descriptor.FieldDescriptorProto_TYPE_STRING,
751 descriptor.FieldDescriptorProto_TYPE_BYTES:
752 // Protect frogs.
753 defaultValue = Quote(defaultValue)
754 // Don't need the quotes
755 defaultValue = defaultValue[1 : len(defaultValue)-1]
756 case descriptor.FieldDescriptorProto_TYPE_ENUM:
757 // For enums we need to provide the integer constant.
758 obj := g.ObjectNamed(proto.GetString(field.TypeName))
759 enum, ok := obj.(*EnumDescriptor)
760 if !ok {
761 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
762 }
763 defaultValue = enum.integerValueAsString(defaultValue)
764 }
765 defaultValue = ",def=" + defaultValue
766 }
767 enum := ""
768 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
769 obj := g.ObjectNamed(proto.GetString(field.TypeName))
770 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
771 }
David Symonds5b7775e2010-12-01 10:09:04 +1100772 packed := ""
773 if field.Options != nil && proto.GetBool(field.Options.Packed) {
774 packed = ",packed"
775 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700776 name := proto.GetString(field.Name)
777 if name == CamelCase(name) {
778 name = ""
779 } else {
780 name = ",name=" + name
781 }
David Symonds5b7775e2010-12-01 10:09:04 +1100782 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s%s)",
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700783 wiretype,
784 proto.GetInt32(field.Number),
785 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +1100786 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700787 name,
788 enum,
789 defaultValue))
790}
791
792func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
793 switch typ {
794 case descriptor.FieldDescriptorProto_TYPE_GROUP:
795 return false
796 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
797 return false
798 case descriptor.FieldDescriptorProto_TYPE_BYTES:
799 return false
800 }
801 return true
802}
803
804// TypeName is the printed name appropriate for an item. If the object is in the current file,
805// TypeName drops the package name and underscores the rest.
806// Otherwise the object is from another package; and the result is the underscored
807// package name followed by the item name.
808// The result always has an initial capital.
809func (g *Generator) TypeName(obj Object) string {
810 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
811}
812
813// TypeNameWithPackage is like TypeName, but always includes the package
814// name even if the object is in our own package.
815func (g *Generator) TypeNameWithPackage(obj Object) string {
816 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
817}
818
819// GoType returns a string representing the type name, and the wire type
820func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
821 // TODO: Options.
822 switch *field.Type {
823 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
824 typ, wire = "float64", "fixed64"
825 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
826 typ, wire = "float32", "fixed32"
827 case descriptor.FieldDescriptorProto_TYPE_INT64:
828 typ, wire = "int64", "varint"
829 case descriptor.FieldDescriptorProto_TYPE_UINT64:
830 typ, wire = "uint64", "varint"
831 case descriptor.FieldDescriptorProto_TYPE_INT32:
832 typ, wire = "int32", "varint"
833 case descriptor.FieldDescriptorProto_TYPE_UINT32:
834 typ, wire = "uint32", "varint"
835 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
836 typ, wire = "uint64", "fixed64"
837 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
838 typ, wire = "uint32", "fixed32"
839 case descriptor.FieldDescriptorProto_TYPE_BOOL:
840 typ, wire = "bool", "varint"
841 case descriptor.FieldDescriptorProto_TYPE_STRING:
842 typ, wire = "string", "bytes"
843 case descriptor.FieldDescriptorProto_TYPE_GROUP:
844 desc := g.ObjectNamed(proto.GetString(field.TypeName))
845 typ, wire = "*"+g.TypeName(desc), "group"
846 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
847 desc := g.ObjectNamed(proto.GetString(field.TypeName))
848 typ, wire = "*"+g.TypeName(desc), "bytes"
849 case descriptor.FieldDescriptorProto_TYPE_BYTES:
850 typ, wire = "[]byte", "bytes"
851 case descriptor.FieldDescriptorProto_TYPE_ENUM:
852 desc := g.ObjectNamed(proto.GetString(field.TypeName))
853 typ, wire = g.TypeName(desc), "varint"
854 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
855 typ, wire = "int32", "fixed32"
856 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
857 typ, wire = "int64", "fixed64"
858 case descriptor.FieldDescriptorProto_TYPE_SINT32:
859 typ, wire = "int32", "zigzag32"
860 case descriptor.FieldDescriptorProto_TYPE_SINT64:
861 typ, wire = "int64", "zigzag64"
862 default:
863 g.Fail("unknown type for", proto.GetString(field.Name))
864 }
865 if isRepeated(field) {
866 typ = "[]" + typ
867 } else if needsStar(*field.Type) {
868 typ = "*" + typ
869 }
870 return
871}
872
David Symondsf90e3382010-05-05 10:53:44 +1000873func (g *Generator) RecordTypeUse(t string) {
874 if obj, ok := g.typeNameToObject[t]; ok {
875 g.usedPackages[obj.PackageName()] = true
876 }
877}
878
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700879// Generate the type and default constant definitions for this Descriptor.
880func (g *Generator) generateMessage(message *Descriptor) {
881 // The full type name
882 typeName := message.TypeName()
883 // The full type name, CamelCased.
884 ccTypeName := CamelCaseSlice(typeName)
885
886 g.P("type ", ccTypeName, " struct {")
887 g.In()
888 for _, field := range message.Field {
889 fieldname := CamelCase(*field.Name)
890 typename, wiretype := g.GoType(message, field)
891 tag := g.goTag(field, wiretype)
892 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000893 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700894 }
895 if len(message.ExtensionRange) > 0 {
896 g.P("XXX_extensions\t\tmap[int32][]byte")
897 }
898 g.P("XXX_unrecognized\t[]byte")
899 g.Out()
900 g.P("}")
901
Rob Pikec6d8e4a2010-07-28 15:34:32 -0700902 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700903 g.P("func (this *", ccTypeName, ") Reset() {")
904 g.In()
905 g.P("*this = ", ccTypeName, "{}")
906 g.Out()
907 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700908
909 // Extension support methods
910 if len(message.ExtensionRange) > 0 {
David Symonds4fee3b12010-11-11 10:00:13 +1100911 // message_set_wire_format only makes sense when extensions are defined.
912 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
913 g.P()
914 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
915 g.In()
916 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
917 g.Out()
918 g.P("}")
919 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
920 g.In()
921 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
922 g.Out()
923 g.P("}")
924 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
925 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
926 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
927 }
928
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700929 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -0700930 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700931 g.In()
932 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -0700933 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
934 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700935 }
936 g.Out()
937 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -0700938 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700939 g.In()
940 g.P("return extRange_", ccTypeName)
941 g.Out()
942 g.P("}")
943 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
944 g.In()
945 g.P("if this.XXX_extensions == nil {")
946 g.In()
947 g.P("this.XXX_extensions = make(map[int32][]byte)")
948 g.Out()
949 g.P("}")
950 g.P("return this.XXX_extensions")
951 g.Out()
952 g.P("}")
953 }
954
955 // Default constants
956 for _, field := range message.Field {
957 def := proto.GetString(field.DefaultValue)
958 if def == "" {
959 continue
960 }
961 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
962 typename, _ := g.GoType(message, field)
963 if typename[0] == '*' {
964 typename = typename[1:]
965 }
966 kind := "const "
967 switch {
968 case typename == "bool":
969 case typename == "string":
970 def = Quote(def)
971 case typename == "[]byte":
972 def = "[]byte(" + Quote(def) + ")"
973 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +1100974 case def == "inf", def == "-inf", def == "nan":
975 // These names are known to, and defined by, the protocol language.
976 switch def {
977 case "inf":
978 def = "math.Inf(1)"
979 case "-inf":
980 def = "math.Inf(-1)"
981 case "nan":
982 def = "math.NaN()"
983 }
984 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
985 def = "float32(" + def + ")"
986 }
987 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700988 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
989 // Must be an enum. Need to construct the prefixed name.
990 obj := g.ObjectNamed(proto.GetString(field.TypeName))
991 enum, ok := obj.(*EnumDescriptor)
992 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -0700993 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700994 continue
995 }
Rob Pike87af39e2010-07-19 10:48:02 -0700996 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700997 }
998 g.P(kind, fieldname, " ", typename, " = ", def)
999 }
1000 g.P()
1001
1002 for _, ext := range message.ext {
1003 g.generateExtension(ext)
1004 }
1005}
1006
1007func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
1008 // The full type name
1009 typeName := ext.TypeName()
1010 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1011 for i, s := range typeName {
1012 typeName[i] = CamelCase(s)
1013 }
1014 ccTypeName := "E_" + strings.Join(typeName, "_")
1015
1016 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1017 field := ext.FieldDescriptorProto
1018 fieldType, wireType := g.GoType(ext.parent, field)
1019 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001020 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001021
Rob Pikec9e7d972010-06-10 10:30:22 -07001022 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001023 g.In()
1024 g.P("ExtendedType: (", extendedType, ")(nil),")
1025 g.P("ExtensionType: (", fieldType, ")(nil),")
1026 g.P("Field: ", field.Number, ",")
1027 g.P("Tag: ", tag, ",")
1028
1029 g.Out()
1030 g.P("}")
1031 g.P()
1032}
1033
1034func (g *Generator) generateInitFunction() {
1035 g.P("func init() {")
1036 g.In()
1037 for _, enum := range g.file.enum {
1038 g.generateEnumRegistration(enum)
1039 }
1040 g.Out()
1041 g.P("}")
1042}
1043
1044func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1045 pkg := g.packageName + "." // We always print the full package name here.
1046 // The full type name
1047 typeName := enum.TypeName()
1048 // The full type name, CamelCased.
1049 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001050 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001051}
1052
1053// And now lots of helper functions.
1054
Rob Pike2c7bafc2010-06-10 16:07:14 -07001055// Is c an ASCII lower-case letter?
1056func isASCIILower(c byte) bool {
1057 return 'a' <= c && c <= 'z'
1058}
1059
1060// Is c an ASCII digit?
1061func isASCIIDigit(c byte) bool {
1062 return '0' <= c && c <= '9'
1063}
1064
1065// CamelCase returns the CamelCased name.
1066// If there is an interior underscore followed by a lower case letter,
1067// drop the underscore and convert the letter to upper case.
1068// There is a remote possibility of this rewrite causing a name collision,
1069// but it's so remote we're prepared to pretend it's nonexistent - since the
1070// C++ generator lowercases names, it's extremely unlikely to have two fields
1071// with different capitalizations.
1072// In short, _my_field_name_2 becomes XMyFieldName2.
1073func CamelCase(s string) string {
1074 if s == "" {
1075 return ""
1076 }
1077 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001078 i := 0
1079 if s[0] == '_' {
1080 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001081 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001082 i++
1083 }
1084 // Invariant: if the next letter is lower case, it must be converted
1085 // to upper case.
1086 // That is, we process a word at a time, where words are marked by _ or
1087 // upper case letter. Digits are treated as words.
1088 for ; i < len(s); i++ {
1089 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001090 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1091 continue // Skip the underscore in s.
1092 }
1093 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001094 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001095 continue
1096 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001097 // Assume we have a letter now - if not, it's a bogus identifier.
1098 // The next word is a sequence of characters that must start upper case.
1099 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001100 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001101 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001102 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001103 // Accept lower case sequence that follows.
1104 for i+1 < len(s) && isASCIILower(s[i+1]) {
1105 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001106 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001107 }
1108 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001109 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001110}
1111
1112// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1113// be joined with "_".
1114func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1115
1116// dottedSlice turns a sliced name into a dotted name.
1117func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1118
1119// Quote returns a Go-source quoted string representation of s.
1120func Quote(s string) string { return fmt.Sprintf("%q", s) }
1121
1122// Given a .proto file name, return the output name for the generated Go program.
1123func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001124 ext := path.Ext(name)
1125 if ext == ".proto" || ext == ".protodevel" {
1126 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001127 }
1128 return name + ".pb.go"
1129}
1130
1131// Is this field optional?
1132func isOptional(field *descriptor.FieldDescriptorProto) bool {
1133 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1134}
1135
1136// Is this field required?
1137func isRequired(field *descriptor.FieldDescriptorProto) bool {
1138 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1139}
1140
1141// Is this field repeated?
1142func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1143 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1144}
1145
1146// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001147// which can be dotted in the input .proto file. It maps dots to underscores.
1148// Because we also get here from package names generated from file names, it also maps
1149// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001150func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001151 switch rune {
1152 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001153 return '_'
1154 }
1155 return rune
1156}
Rob Pikec9e7d972010-06-10 10:30:22 -07001157
1158// BaseName returns the last path element of the name, with the last dotted suffix removed.
1159func BaseName(name string) string {
1160 // First, find the last element
1161 if i := strings.LastIndex(name, "/"); i >= 0 {
1162 name = name[i+1:]
1163 }
1164 // Now drop the suffix
1165 if i := strings.LastIndex(name, "."); i >= 0 {
1166 name = name[0:i]
1167 }
1168 return name
1169}