blob: cbef5ede7cc4e04114a8bd83b3936b7edc48c97f [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.
36
37 Not supported yet:
38 services
39 options
40*/
41package generator
42
43import (
44 "bytes"
45 "fmt"
46 "log"
47 "os"
48 "strings"
49 "unicode"
50
51 "goprotobuf.googlecode.com/hg/proto"
52 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
53 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
54)
55
56// A Plugin provides functionality to add to the output during Go code generation,
57// such as to produce RPC stubs.
58type Plugin interface {
59 // Name identifies the plugin.
60 Name() string
61 // Generate produces the code generated by the plugin for this file, except for the imports,
62 // by calling the generator's methods P, In, and Out.
63 Generate(g *Generator, file *FileDescriptor)
64 // GenerateImports produces the import declarations for this file.
65 GenerateImports(g *Generator, file *FileDescriptor)
66}
67
68var plugins []Plugin
69
70// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
71// It is typically called during initialization.
72func RegisterPlugin(p Plugin) {
73 n := len(plugins)
74 if cap(plugins) == n {
75 nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
76 copy(nplugins, plugins)
77 plugins = nplugins
78 }
79 plugins = plugins[0:n+1]
80 plugins[n] = p
81 log.Stderr("installed plugin:", p.Name())
82}
83
84// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
85// a pointer to the FileDescriptorProto that represents it. These types achieve that
86// wrapping by placing each Proto inside a struct with the pointer to its File. The
87// structs have the same names as their contents, with "Proto" removed.
88// FileDescriptor is used to store the things that it points to.
89
90// The file and package name method are common to messages and enums.
91type common struct {
92 File *descriptor.FileDescriptorProto // File this object comes from.
93}
94
95// PackageName is name in the package clause in the generated file.
96func (c *common) PackageName() string { return uniquePackageOf(c.File) }
97
98// Descriptor represents a protocol buffer message.
99type Descriptor struct {
100 common
101 *descriptor.DescriptorProto
102 parent *Descriptor // The containing message, if any.
103 nested []*Descriptor // Inner messages, if any.
104 ext []*ExtensionDescriptor // Extensions, if any.
105 typename []string // Cached typename vector.
106}
107
108// TypeName returns the elements of the dotted type name.
109// The package name is not part of this name.
110func (d *Descriptor) TypeName() []string {
111 if d.typename != nil {
112 return d.typename
113 }
114 n := 0
115 for parent := d; parent != nil; parent = parent.parent {
116 n++
117 }
118 s := make([]string, n, n)
119 for parent := d; parent != nil; parent = parent.parent {
120 n--
121 s[n] = proto.GetString(parent.Name)
122 }
123 d.typename = s
124 return s
125}
126
127// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
128// Otherwise it will be the descriptor of the message in which it is defined.
129type EnumDescriptor struct {
130 common
131 *descriptor.EnumDescriptorProto
132 parent *Descriptor // The containing message, if any.
133 typename []string // Cached typename vector.
134}
135
136// TypeName returns the elements of the dotted type name.
137// The package name is not part of this name.
138func (e *EnumDescriptor) TypeName() (s []string) {
139 if e.typename != nil {
140 return e.typename
141 }
142 name := proto.GetString(e.Name)
143 if e.parent == nil {
144 s = make([]string, 1)
145 } else {
146 pname := e.parent.TypeName()
147 s = make([]string, len(pname)+1)
148 copy(s, pname)
149 }
150 s[len(s)-1] = name
151 e.typename = s
152 return s
153}
154
155// Everything but the last element of the full type name, CamelCased.
156// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
157func (e *EnumDescriptor) prefix() string {
158 typeName := e.TypeName()
159 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
160 if e.parent == nil {
161 // If the enum is not part of a message, the prefix is just the type name.
162 ccPrefix = CamelCase(*e.Name) + "_"
163 }
164 return ccPrefix
165}
166
167// The integer value of the named constant in this enumerated type.
168func (e *EnumDescriptor) integerValueAsString(name string) string {
169 for _, c := range e.Value {
170 if proto.GetString(c.Name) == name {
171 return fmt.Sprint(proto.GetInt32(c.Number))
172 }
173 }
174 log.Exit("cannot find value for enum constant")
175 return ""
176}
177
178// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
179// Otherwise it will be the descriptor of the message in which it is defined.
180type ExtensionDescriptor struct {
181 common
182 *descriptor.FieldDescriptorProto
183 parent *Descriptor // The containing message, if any.
184}
185
186// TypeName returns the elements of the dotted type name.
187// The package name is not part of this name.
188func (e *ExtensionDescriptor) TypeName() (s []string) {
189 name := proto.GetString(e.Name)
190 if e.parent == nil {
191 // top-level extension
192 s = make([]string, 1)
193 } else {
194 pname := e.parent.TypeName()
195 s = make([]string, len(pname)+1)
196 copy(s, pname)
197 }
198 s[len(s)-1] = name
199 return s
200}
201
202// FileDescriptor describes an protocol buffer descriptor file (.proto).
203// It includes slices of all the messages and enums defined within it.
204// Those slices are constructed by WrapTypes.
205type FileDescriptor struct {
206 *descriptor.FileDescriptorProto
207 desc []*Descriptor // All the messages defined in this file.
208 enum []*EnumDescriptor // All the enums defined in this file.
209 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
210}
211
212// PackageName is the package name we'll use in the generated code to refer to this file.
213func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
214
215// The package named defined in the input for this file, possibly dotted.
216func (d *FileDescriptor) originalPackageName() string {
217 return proto.GetString(d.Package)
218}
219
220// Whether the proto library needs importing.
221// This will be true if there are any enums, extensions, or messages with extension ranges.
222func (d *FileDescriptor) needProtoImport() bool {
223 if len(d.enum) > 0 || len(d.ext) > 0 {
224 return true
225 }
226 for _, desc := range d.desc {
227 if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
228 return true
229 }
230 }
231 return false
232}
233
234// Object is an interface abstracting the abilities shared by enums and messages.
235type Object interface {
236 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
237 TypeName() []string
238}
239
240// Each package name we generate must be unique. The package we're generating
241// gets its own name but every other package must have a unqiue name that does
242// not conflict in the code we generate. These names are chosen globally (although
243// they don't have to be, it simplifies things to do them globally).
244func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
245 s, ok := uniquePackageName[fd]
246 if !ok {
247 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
248 }
249 return s
250}
251
252// Generator is the type whose methods generate the output, stored in the associated response structure.
253type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000254 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700255
256 Request *plugin.CodeGeneratorRequest // The input.
257 Response *plugin.CodeGeneratorResponse // The output.
258
259 packageName string // What we're calling ourselves.
260 allFiles []*FileDescriptor // All files in the tree
261 genFiles []*FileDescriptor // Those files we will generate output for.
262 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000263 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700264 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
265 indent string
266}
267
268// New creates a new generator and allocates the request and response protobufs.
269func New() *Generator {
270 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000271 g.Buffer = new(bytes.Buffer)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700272 g.Request = plugin.NewCodeGeneratorRequest()
273 g.Response = plugin.NewCodeGeneratorResponse()
274 return g
275}
276
277// Error reports a problem, including an os.Error, and exits the program.
278func (g *Generator) Error(err os.Error, msgs ...string) {
279 s := strings.Join(msgs, " ") + ":" + err.String()
280 log.Stderr("protoc-gen-go: error: ", s)
281 g.Response.Error = proto.String(s)
282 os.Exit(1)
283}
284
285// Fail reports a problem and exits the program.
286func (g *Generator) Fail(msgs ...string) {
287 s := strings.Join(msgs, " ")
288 log.Stderr("protoc-gen-go: error: ", s)
289 g.Response.Error = proto.String(s)
290 os.Exit(1)
291}
292
293// DefaultPackageName returns the package name printed for the object.
294// If its file is in a different package, it returns the package name we're using for this file, plus ".".
295// Otherwise it returns the empty string.
296func (g *Generator) DefaultPackageName(obj Object) string {
297 pkg := obj.PackageName()
298 if pkg == g.packageName {
299 return ""
300 }
301 return pkg + "."
302}
303
304// For each input file, the unique package name to use, underscored.
305var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
306
307// SetPackageNames Sets the package name for this run.
308// The package name must agree across all files being generated.
309// It also defines unique package names for all imported files.
310func (g *Generator) SetPackageNames() {
311 inUse := make(map[string]bool)
312 pkg := proto.GetString(g.genFiles[0].Package)
313 g.packageName = strings.Map(DotToUnderscore, pkg)
314 inUse[pkg] = true
315 for _, f := range g.genFiles {
316 thisPkg := proto.GetString(f.Package)
317 if thisPkg != pkg {
318 g.Fail("inconsistent package names:", thisPkg, pkg)
319 }
320 }
321AllFiles:
322 for _, f := range g.allFiles {
323 for _, genf := range g.genFiles {
324 if f == genf {
325 // In this package already.
326 uniquePackageName[f.FileDescriptorProto] = g.packageName
327 continue AllFiles
328 }
329 }
330 truePkg := proto.GetString(f.Package)
331 pkg := truePkg
332 for {
333 _, present := inUse[pkg]
334 if present {
335 // It's a duplicate; must rename.
336 pkg += "X"
337 continue
338 }
339 break
340 }
341 // Install it.
342 inUse[pkg] = true
343 uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
344 }
345}
346
347// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
348// and FileDescriptorProtos into file-referenced objects within the Generator.
349// It also creates the list of files to generate and so should be called before GenerateAllFiles.
350func (g *Generator) WrapTypes() {
351 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
352 for i, f := range g.Request.ProtoFile {
353 pkg := proto.GetString(f.Package)
354 if pkg == "" {
355 g.Fail(proto.GetString(f.Name), "is missing a package declaration")
356 }
357 // We must wrap the descriptors before we wrap the enums
358 descs := wrapDescriptors(f)
359 g.buildNestedDescriptors(descs)
360 enums := wrapEnumDescriptors(f, descs)
361 exts := wrapExtensions(f)
362 g.allFiles[i] = &FileDescriptor{
363 FileDescriptorProto: f,
364 desc: descs,
365 enum: enums,
366 ext: exts,
367 }
368 }
369
370 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
371FindFiles:
372 for i, fileName := range g.Request.FileToGenerate {
373 // Search the list. This algorithm is n^2 but n is tiny.
374 for _, file := range g.allFiles {
375 if fileName == proto.GetString(file.Name) {
376 g.genFiles[i] = file
377 continue FindFiles
378 }
379 }
380 g.Fail("could not find file named", fileName)
381 }
382 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
383}
384
385// Scan the descriptors in this file. For each one, build the slice of nested descriptors
386func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
387 for _, desc := range descs {
388 if len(desc.NestedType) != 0 {
389 desc.nested = make([]*Descriptor, len(desc.NestedType))
390 n := 0
391 for _, nest := range descs {
392 if nest.parent == desc {
393 desc.nested[n] = nest
394 n++
395 }
396 }
397 if n != len(desc.NestedType) {
398 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
399 }
400 }
401 }
402}
403
404// Construct the Descriptor and add it to the slice
405func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
406 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
407
408 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
409 for i, field := range desc.Extension {
410 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
411 }
412
413 if len(sl) == cap(sl) {
414 nsl := make([]*Descriptor, len(sl), 2*len(sl))
415 copy(nsl, sl)
416 sl = nsl
417 }
418 sl = sl[0 : len(sl)+1]
419 sl[len(sl)-1] = d
420 return sl
421}
422
423// Return a slice of all the Descriptors defined within this file
424func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
425 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
426 for _, desc := range file.MessageType {
427 sl = wrapThisDescriptor(sl, desc, nil, file)
428 }
429 return sl
430}
431
432// Wrap this Descriptor, recursively
433func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
434 sl = addDescriptor(sl, desc, parent, file)
435 me := sl[len(sl)-1]
436 for _, nested := range desc.NestedType {
437 sl = wrapThisDescriptor(sl, nested, me, file)
438 }
439 return sl
440}
441
442// Construct the EnumDescriptor and add it to the slice
443func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
444 if len(sl) == cap(sl) {
445 nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
446 copy(nsl, sl)
447 sl = nsl
448 }
449 sl = sl[0 : len(sl)+1]
450 sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
451 return sl
452}
453
454// Return a slice of all the EnumDescriptors defined within this file
455func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
456 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
457 for _, enum := range file.EnumType {
458 sl = addEnumDescriptor(sl, enum, nil, file)
459 }
460 for _, nested := range descs {
461 sl = wrapEnumDescriptorsInMessage(sl, nested, file)
462 }
463 return sl
464}
465
466// Wrap this EnumDescriptor, recursively
467func wrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
468 for _, enum := range desc.EnumType {
469 sl = addEnumDescriptor(sl, enum, desc, file)
470 }
471 for _, nested := range desc.nested {
472 sl = wrapEnumDescriptorsInMessage(sl, nested, file)
473 }
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
486// BuildTypeNameMap builds the map from fully qualified type names to objects.
487// 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 {
492 dottedPkg := "." + f.originalPackageName() + "."
493 for _, enum := range f.enum {
494 name := dottedPkg + dottedSlice(enum.TypeName())
495 g.typeNameToObject[name] = enum
496 }
497 for _, desc := range f.desc {
498 name := dottedPkg + dottedSlice(desc.TypeName())
499 g.typeNameToObject[name] = desc
500 }
501 }
502}
503
504// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
505// returns the descriptor for the message or enum with that name.
506func (g *Generator) ObjectNamed(typeName string) Object {
507 f, ok := g.typeNameToObject[typeName]
508 if !ok {
509 g.Fail("can't find object with type", typeName)
510 }
511 return f
512}
513
514// P prints the arguments to the generated output. It handles strings and int32s, plus
515// handling indirections because they may be *string, etc.
516func (g *Generator) P(str ...interface{}) {
517 g.WriteString(g.indent)
518 for _, v := range str {
519 switch s := v.(type) {
520 case string:
521 g.WriteString(s)
522 case *string:
523 g.WriteString(*s)
524 case *int32:
525 g.WriteString(fmt.Sprintf("%d", *s))
526 default:
527 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
528 }
529 }
530 g.WriteByte('\n')
531}
532
533// In Indents the output one tab stop.
534func (g *Generator) In() { g.indent += "\t" }
535
536// Out unindents the output one tab stop.
537func (g *Generator) Out() {
538 if len(g.indent) > 0 {
539 g.indent = g.indent[1:]
540 }
541}
542
543// GenerateAllFiles generates the output for all the files we're outputting.
544func (g *Generator) GenerateAllFiles() {
545 for i, file := range g.genFiles {
546 g.Reset()
547 g.generate(file)
548 g.runPlugins(file)
549 g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
550 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
551 g.Response.File[i].Content = proto.String(g.String())
552 }
553}
554
555// Run all the plugins associated with the file.
556func (g *Generator) runPlugins(file *FileDescriptor) {
557 for _, p := range plugins {
558 p.Generate(g, file)
559 }
560}
561
562
563// FileOf return the FileDescriptor for this FileDescriptorProto.
564func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
565 for _, file := range g.allFiles {
566 if file.FileDescriptorProto == fd {
567 return file
568 }
569 }
570 g.Fail("could not find file in table:", proto.GetString(fd.Name))
571 return nil
572}
573
574// Fill the response protocol buffer with the generated output for all the files we're
575// supposed to generate.
576func (g *Generator) generate(file *FileDescriptor) {
577 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000578 g.usedPackages = make(map[string]bool)
579
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700580 for _, enum := range g.file.enum {
581 g.generateEnum(enum)
582 }
583 for _, desc := range g.file.desc {
584 g.generateMessage(desc)
585 }
586 for _, ext := range g.file.ext {
587 g.generateExtension(ext)
588 }
589 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000590
591 // Generate header and imports last, though they appear first in the output.
592 rem := g.Buffer
593 g.Buffer = new(bytes.Buffer)
594 g.generateHeader()
595 g.generateImports()
596 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700597}
598
599// Generate the header, including package definition and imports
600func (g *Generator) generateHeader() {
601 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
602 g.P("// DO NOT EDIT!")
603 g.P()
604 g.P("package ", g.file.PackageName())
605 g.P()
606}
607
608// Generate the header, including package definition and imports
609func (g *Generator) generateImports() {
610 if g.file.needProtoImport() {
611 g.P(`import "goprotobuf.googlecode.com/hg/proto"`)
612 }
613 for _, s := range g.file.Dependency {
614 // Need to find the descriptor for this file
615 for _, fd := range g.allFiles {
616 if proto.GetString(fd.Name) == s {
617 filename := goFileName(s)
618 if strings.HasSuffix(filename, ".go") {
619 filename = filename[0:len(filename)-3]
620 }
David Symondsf90e3382010-05-05 10:53:44 +1000621 if _, ok := g.usedPackages[fd.PackageName()]; ok {
622 g.P("import ", fd.PackageName(), " ", Quote(filename))
623 } else {
624 log.Stderr("protoc-gen-go: discarding unused import: ", filename)
625 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700626 break
627 }
628 }
629 }
630 g.P()
631 // TODO: may need to worry about uniqueness across plugins
632 for _, p := range plugins {
633 p.GenerateImports(g, g.file)
634 g.P()
635 }
636}
637
638// Generate the enum definitions for this EnumDescriptor.
639func (g *Generator) generateEnum(enum *EnumDescriptor) {
640 // The full type name
641 typeName := enum.TypeName()
642 // The full type name, CamelCased.
643 ccTypeName := CamelCaseSlice(typeName)
644 ccPrefix := enum.prefix()
645 g.P("type ", ccTypeName, " int32")
646 g.P("const (")
647 g.In()
648 for _, e := range enum.Value {
649 g.P(ccPrefix+*e.Name, " = ", e.Number)
650 }
651 g.Out()
652 g.P(")")
653 g.P("var ", ccTypeName, "_name = map[int32] string {")
654 g.In()
655 generated := make(map[int32] bool) // avoid duplicate values
656 for _, e := range enum.Value {
657 duplicate := ""
658 if _, present := generated[*e.Number]; present {
659 duplicate = "// Duplicate value: "
660 }
661 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
662 generated[*e.Number] = true
663 }
664 g.Out()
665 g.P("}")
666 g.P("var ", ccTypeName, "_value = map[string] int32 {")
667 g.In()
668 for _, e := range enum.Value {
669 g.P(Quote(*e.Name), ": ", e.Number, ",")
670 }
671 g.Out()
672 g.P("}")
673 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
674 g.In()
675 g.P("e := ", ccTypeName, "(x)")
676 g.P("return &e")
677 g.Out()
678 g.P("}")
679 g.P()
680}
681
682// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
683// identifies details of the field for the protocol buffer marshaling and unmarshaling
684// code. The fields are:
685// wire encoding
686// protocol tag number
687// opt,req,rep for optional, required, or repeated
688// name= the original declared name
689// enum= the name of the enum type if it is an enum-typed field.
690// def= string representation of the default value, if any.
691// The default value must be in a representation that can be used at run-time
692// to generate the default value. Thus bools become 0 and 1, for instance.
693func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
694 optrepreq := ""
695 switch {
696 case isOptional(field):
697 optrepreq = "opt"
698 case isRequired(field):
699 optrepreq = "req"
700 case isRepeated(field):
701 optrepreq = "rep"
702 }
703 defaultValue := proto.GetString(field.DefaultValue)
704 if defaultValue != "" {
705 switch *field.Type {
706 case descriptor.FieldDescriptorProto_TYPE_BOOL:
707 if defaultValue == "true" {
708 defaultValue = "1"
709 } else {
710 defaultValue = "0"
711 }
712 case descriptor.FieldDescriptorProto_TYPE_STRING,
713 descriptor.FieldDescriptorProto_TYPE_BYTES:
714 // Protect frogs.
715 defaultValue = Quote(defaultValue)
716 // Don't need the quotes
717 defaultValue = defaultValue[1 : len(defaultValue)-1]
718 case descriptor.FieldDescriptorProto_TYPE_ENUM:
719 // For enums we need to provide the integer constant.
720 obj := g.ObjectNamed(proto.GetString(field.TypeName))
721 enum, ok := obj.(*EnumDescriptor)
722 if !ok {
723 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
724 }
725 defaultValue = enum.integerValueAsString(defaultValue)
726 }
727 defaultValue = ",def=" + defaultValue
728 }
729 enum := ""
730 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
731 obj := g.ObjectNamed(proto.GetString(field.TypeName))
732 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
733 }
734 name := proto.GetString(field.Name)
735 if name == CamelCase(name) {
736 name = ""
737 } else {
738 name = ",name=" + name
739 }
740 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
741 wiretype,
742 proto.GetInt32(field.Number),
743 optrepreq,
744 name,
745 enum,
746 defaultValue))
747}
748
749func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
750 switch typ {
751 case descriptor.FieldDescriptorProto_TYPE_GROUP:
752 return false
753 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
754 return false
755 case descriptor.FieldDescriptorProto_TYPE_BYTES:
756 return false
757 }
758 return true
759}
760
761// TypeName is the printed name appropriate for an item. If the object is in the current file,
762// TypeName drops the package name and underscores the rest.
763// Otherwise the object is from another package; and the result is the underscored
764// package name followed by the item name.
765// The result always has an initial capital.
766func (g *Generator) TypeName(obj Object) string {
767 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
768}
769
770// TypeNameWithPackage is like TypeName, but always includes the package
771// name even if the object is in our own package.
772func (g *Generator) TypeNameWithPackage(obj Object) string {
773 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
774}
775
776// GoType returns a string representing the type name, and the wire type
777func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
778 // TODO: Options.
779 switch *field.Type {
780 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
781 typ, wire = "float64", "fixed64"
782 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
783 typ, wire = "float32", "fixed32"
784 case descriptor.FieldDescriptorProto_TYPE_INT64:
785 typ, wire = "int64", "varint"
786 case descriptor.FieldDescriptorProto_TYPE_UINT64:
787 typ, wire = "uint64", "varint"
788 case descriptor.FieldDescriptorProto_TYPE_INT32:
789 typ, wire = "int32", "varint"
790 case descriptor.FieldDescriptorProto_TYPE_UINT32:
791 typ, wire = "uint32", "varint"
792 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
793 typ, wire = "uint64", "fixed64"
794 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
795 typ, wire = "uint32", "fixed32"
796 case descriptor.FieldDescriptorProto_TYPE_BOOL:
797 typ, wire = "bool", "varint"
798 case descriptor.FieldDescriptorProto_TYPE_STRING:
799 typ, wire = "string", "bytes"
800 case descriptor.FieldDescriptorProto_TYPE_GROUP:
801 desc := g.ObjectNamed(proto.GetString(field.TypeName))
802 typ, wire = "*"+g.TypeName(desc), "group"
803 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
804 desc := g.ObjectNamed(proto.GetString(field.TypeName))
805 typ, wire = "*"+g.TypeName(desc), "bytes"
806 case descriptor.FieldDescriptorProto_TYPE_BYTES:
807 typ, wire = "[]byte", "bytes"
808 case descriptor.FieldDescriptorProto_TYPE_ENUM:
809 desc := g.ObjectNamed(proto.GetString(field.TypeName))
810 typ, wire = g.TypeName(desc), "varint"
811 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
812 typ, wire = "int32", "fixed32"
813 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
814 typ, wire = "int64", "fixed64"
815 case descriptor.FieldDescriptorProto_TYPE_SINT32:
816 typ, wire = "int32", "zigzag32"
817 case descriptor.FieldDescriptorProto_TYPE_SINT64:
818 typ, wire = "int64", "zigzag64"
819 default:
820 g.Fail("unknown type for", proto.GetString(field.Name))
821 }
822 if isRepeated(field) {
823 typ = "[]" + typ
824 } else if needsStar(*field.Type) {
825 typ = "*" + typ
826 }
827 return
828}
829
David Symondsf90e3382010-05-05 10:53:44 +1000830func (g *Generator) RecordTypeUse(t string) {
831 if obj, ok := g.typeNameToObject[t]; ok {
832 g.usedPackages[obj.PackageName()] = true
833 }
834}
835
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700836// Generate the type and default constant definitions for this Descriptor.
837func (g *Generator) generateMessage(message *Descriptor) {
838 // The full type name
839 typeName := message.TypeName()
840 // The full type name, CamelCased.
841 ccTypeName := CamelCaseSlice(typeName)
842
843 g.P("type ", ccTypeName, " struct {")
844 g.In()
845 for _, field := range message.Field {
846 fieldname := CamelCase(*field.Name)
847 typename, wiretype := g.GoType(message, field)
848 tag := g.goTag(field, wiretype)
849 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000850 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700851 }
852 if len(message.ExtensionRange) > 0 {
853 g.P("XXX_extensions\t\tmap[int32][]byte")
854 }
855 g.P("XXX_unrecognized\t[]byte")
856 g.Out()
857 g.P("}")
858
859 // Reset and New functions
860 g.P("func (this *", ccTypeName, ") Reset() {")
861 g.In()
862 g.P("*this = ", ccTypeName, "{}")
863 g.Out()
864 g.P("}")
865 g.P("func New", ccTypeName, "() *", ccTypeName, " {")
866 g.In()
867 g.P("return new(", ccTypeName, ")")
868 g.Out()
869 g.P("}")
870
871 // Extension support methods
872 if len(message.ExtensionRange) > 0 {
873 g.P()
874 g.P("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
875 g.In()
876 for _, r := range message.ExtensionRange {
877 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
878 g.P("proto.ExtensionRange{", r.Start, ", ", end, "},")
879 }
880 g.Out()
881 g.P("}")
882 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
883 g.In()
884 g.P("return extRange_", ccTypeName)
885 g.Out()
886 g.P("}")
887 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
888 g.In()
889 g.P("if this.XXX_extensions == nil {")
890 g.In()
891 g.P("this.XXX_extensions = make(map[int32][]byte)")
892 g.Out()
893 g.P("}")
894 g.P("return this.XXX_extensions")
895 g.Out()
896 g.P("}")
897 }
898
899 // Default constants
900 for _, field := range message.Field {
901 def := proto.GetString(field.DefaultValue)
902 if def == "" {
903 continue
904 }
905 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
906 typename, _ := g.GoType(message, field)
907 if typename[0] == '*' {
908 typename = typename[1:]
909 }
910 kind := "const "
911 switch {
912 case typename == "bool":
913 case typename == "string":
914 def = Quote(def)
915 case typename == "[]byte":
916 def = "[]byte(" + Quote(def) + ")"
917 kind = "var "
918 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
919 // Must be an enum. Need to construct the prefixed name.
920 obj := g.ObjectNamed(proto.GetString(field.TypeName))
921 enum, ok := obj.(*EnumDescriptor)
922 if !ok {
923 log.Stderr("don't know how to generate constant for", fieldname)
924 continue
925 }
926 def = enum.prefix() + def
927 }
928 g.P(kind, fieldname, " ", typename, " = ", def)
929 }
930 g.P()
931
932 for _, ext := range message.ext {
933 g.generateExtension(ext)
934 }
935}
936
937func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
938 // The full type name
939 typeName := ext.TypeName()
940 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
941 for i, s := range typeName {
942 typeName[i] = CamelCase(s)
943 }
944 ccTypeName := "E_" + strings.Join(typeName, "_")
945
946 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
947 field := ext.FieldDescriptorProto
948 fieldType, wireType := g.GoType(ext.parent, field)
949 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +1000950 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700951
952 g.P("var ", ccTypeName, " = &proto.ExtensionDesc{")
953 g.In()
954 g.P("ExtendedType: (", extendedType, ")(nil),")
955 g.P("ExtensionType: (", fieldType, ")(nil),")
956 g.P("Field: ", field.Number, ",")
957 g.P("Tag: ", tag, ",")
958
959 g.Out()
960 g.P("}")
961 g.P()
962}
963
964func (g *Generator) generateInitFunction() {
965 g.P("func init() {")
966 g.In()
967 for _, enum := range g.file.enum {
968 g.generateEnumRegistration(enum)
969 }
970 g.Out()
971 g.P("}")
972}
973
974func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
975 pkg := g.packageName + "." // We always print the full package name here.
976 // The full type name
977 typeName := enum.TypeName()
978 // The full type name, CamelCased.
979 ccTypeName := CamelCaseSlice(typeName)
980 g.P("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
981}
982
983// And now lots of helper functions.
984
985// CamelCase returns the CamelCased name. Given foo_bar_Baz, the result is FooBar_Baz.
986func CamelCase(name string) string {
987 elems := strings.Split(name, "_", 0)
988 for i, e := range elems {
989 if e == "" {
990 elems[i] = "_"
991 continue
992 }
993 runes := []int(e)
994 if unicode.IsLower(runes[0]) {
995 runes[0] = unicode.ToUpper(runes[0])
996 elems[i] = string(runes)
997 } else {
998 if i > 0 {
999 elems[i] = "_" + e
1000 }
1001 }
1002 }
1003 s := strings.Join(elems, "")
1004 // Name must not begin with an underscore.
1005 if len(s) > 0 && s[0] == '_' {
1006 s = "X" + s[1:]
1007 }
1008 return s
1009}
1010
1011// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1012// be joined with "_".
1013func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1014
1015// dottedSlice turns a sliced name into a dotted name.
1016func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1017
1018// Quote returns a Go-source quoted string representation of s.
1019func Quote(s string) string { return fmt.Sprintf("%q", s) }
1020
1021// Given a .proto file name, return the output name for the generated Go program.
1022func goFileName(name string) string {
1023 if strings.HasSuffix(name, ".proto") {
1024 name = name[0 : len(name)-6]
1025 }
1026 return name + ".pb.go"
1027}
1028
1029// Is this field optional?
1030func isOptional(field *descriptor.FieldDescriptorProto) bool {
1031 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1032}
1033
1034// Is this field required?
1035func isRequired(field *descriptor.FieldDescriptorProto) bool {
1036 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1037}
1038
1039// Is this field repeated?
1040func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1041 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1042}
1043
1044// DotToUnderscore is the mapping function used to generate Go names from package names,
1045// which can be dotted in the input .proto file.
1046func DotToUnderscore(rune int) int {
1047 if rune == '.' {
1048 return '_'
1049 }
1050 return rune
1051}