blob: 1ca46e8b74b6f9c0ba34938e9e2eafa054c4454f [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -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
32package proto
33
34// Functions for writing the Text protocol buffer format.
David Symonds9f402812011-04-28 18:08:44 +100035// TODO: message sets, extensions.
Rob Pikeaaa3a622010-03-20 22:32:34 -070036
37import (
38 "bytes"
39 "fmt"
40 "io"
41 "os"
42 "reflect"
43 "strconv"
44 "strings"
45)
46
47// An io.Writer wrapper that tracks its indentation level.
48type textWriter struct {
49 indent_level int
50 complete bool // if the current position is a complete line
51 compact bool // whether to write out as a one-liner
52 writer io.Writer
David Symonds9f402812011-04-28 18:08:44 +100053
54 c [1]byte // scratch
Rob Pikeaaa3a622010-03-20 22:32:34 -070055}
56
57func (w *textWriter) Write(p []byte) (n int, err os.Error) {
58 n, err = len(p), nil
59
Rob Pike53385442010-06-30 22:22:43 -070060 frags := strings.Split(string(p), "\n", -1)
Rob Pikeaaa3a622010-03-20 22:32:34 -070061 if w.compact {
62 w.writer.Write([]byte(strings.Join(frags, " ")))
63 return
64 }
65
66 for i := 0; i < len(frags); i++ {
67 if w.complete {
68 for j := 0; j < w.indent_level; j++ {
69 w.writer.Write([]byte{' ', ' '})
70 }
71 w.complete = false
72 }
73
74 w.writer.Write([]byte(frags[i]))
75 if i+1 < len(frags) {
76 w.writer.Write([]byte{'\n'})
77 }
78 }
79 w.complete = len(frags[len(frags)-1]) == 0
80
81 return
82}
83
David Symonds9f402812011-04-28 18:08:44 +100084func (w *textWriter) WriteByte(c byte) os.Error {
85 w.c[0] = c
86 _, err := w.Write(w.c[:])
87 return err
88}
89
Rob Pikeaaa3a622010-03-20 22:32:34 -070090func (w *textWriter) indent() { w.indent_level++ }
91
92func (w *textWriter) unindent() {
93 if w.indent_level == 0 {
94 fmt.Fprintln(os.Stderr, "proto: textWriter unindented too far!")
95 } else {
96 w.indent_level--
97 }
98}
99
David Symonds9f402812011-04-28 18:08:44 +1000100func writeName(w *textWriter, props *Properties) {
101 io.WriteString(w, props.OrigName)
102 if props.Wire != "group" {
103 w.WriteByte(':')
104 }
105}
106
Rob Pike97e934d2011-04-11 12:52:49 -0700107func writeStruct(w *textWriter, sv reflect.Value) {
108 st := sv.Type()
Rob Pikeaaa3a622010-03-20 22:32:34 -0700109 sprops := GetProperties(st)
110 for i := 0; i < sv.NumField(); i++ {
111 if strings.HasPrefix(st.Field(i).Name, "XXX_") {
112 continue
113 }
114 props := sprops.Prop[i]
115 fv := sv.Field(i)
Rob Pikeac8b1ce2011-04-11 16:14:54 -0700116 if fv.Kind() == reflect.Ptr && fv.IsNil() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700117 // Field not filled in. This could be an optional field or
118 // a required field that wasn't filled in. Either way, there
119 // isn't anything we can show for it.
120 continue
121 }
Rob Pikeac8b1ce2011-04-11 16:14:54 -0700122 if fv.Kind() == reflect.Slice && fv.IsNil() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700123 // Repeated field that is empty, or a bytes field that is unused.
124 continue
125 }
126
127 if props.Repeated {
Rob Pike97e934d2011-04-11 12:52:49 -0700128 if av := fv; av.Kind() == reflect.Slice {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700129 // Repeated field.
130 for j := 0; j < av.Len(); j++ {
David Symonds9f402812011-04-28 18:08:44 +1000131 writeName(w, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700132 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000133 w.WriteByte(' ')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700134 }
David Symonds9f402812011-04-28 18:08:44 +1000135 writeAny(w, av.Index(j), props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700136 fmt.Fprint(w, "\n")
137 }
138 continue
139 }
140 }
141
David Symonds9f402812011-04-28 18:08:44 +1000142 writeName(w, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700143 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000144 w.WriteByte(' ')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700145 }
146 if len(props.Enum) == 0 || !tryWriteEnum(w, props.Enum, fv) {
David Symonds9f402812011-04-28 18:08:44 +1000147 writeAny(w, fv, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700148 }
149 fmt.Fprint(w, "\n")
150 }
151}
152
153func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool {
Rob Pikeab5b8022010-06-21 17:47:58 -0700154 v = reflect.Indirect(v)
155 if v.Type().Kind() != reflect.Int32 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700156 return false
157 }
158 m, ok := enumNameMaps[enum]
159 if !ok {
160 return false
161 }
Rob Pike97e934d2011-04-11 12:52:49 -0700162 str, ok := m[int32(v.Int())]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700163 if !ok {
164 return false
165 }
166 fmt.Fprintf(w, str)
167 return true
168}
169
David Symonds9f402812011-04-28 18:08:44 +1000170func writeAny(w *textWriter, v reflect.Value, props *Properties) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700171 v = reflect.Indirect(v)
172
173 // We don't attempt to serialise every possible value type; only those
174 // that can occur in protocol buffers, plus a few extra that were easy.
Rob Pike97e934d2011-04-11 12:52:49 -0700175 switch val := v; val.Kind() {
176 case reflect.Slice:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700177 // Should only be a []byte; repeated fields are handled in writeStruct.
Rob Pikeab5b8022010-06-21 17:47:58 -0700178 // TODO: Handle other cases more cleanly.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700179 bytes := make([]byte, val.Len())
180 for i := 0; i < val.Len(); i++ {
Rob Pike97e934d2011-04-11 12:52:49 -0700181 bytes[i] = byte(val.Index(i).Uint())
Rob Pikeaaa3a622010-03-20 22:32:34 -0700182 }
183 // TODO: Should be strconv.QuoteC, which doesn't exist yet
184 fmt.Fprint(w, strconv.Quote(string(bytes)))
Rob Pike97e934d2011-04-11 12:52:49 -0700185 case reflect.String:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700186 // TODO: Should be strconv.QuoteC, which doesn't exist yet
Rob Pike97e934d2011-04-11 12:52:49 -0700187 fmt.Fprint(w, strconv.Quote(val.String()))
188 case reflect.Struct:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700189 // Required/optional group/message.
David Symonds9f402812011-04-28 18:08:44 +1000190 var bra, ket byte = '<', '>'
191 if props != nil && props.Wire == "group" {
192 bra, ket = '{', '}'
193 }
194 w.WriteByte(bra)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700195 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000196 w.WriteByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700197 }
198 w.indent()
199 writeStruct(w, val)
200 w.unindent()
David Symonds9f402812011-04-28 18:08:44 +1000201 w.WriteByte(ket)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700202 default:
203 fmt.Fprint(w, val.Interface())
204 }
205}
206
207func marshalText(w io.Writer, pb interface{}, compact bool) {
David Symonds03c9d412010-08-26 14:23:18 +1000208 if pb == nil {
209 w.Write([]byte("<nil>"))
210 return
211 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700212 aw := new(textWriter)
213 aw.writer = w
214 aw.complete = true
215 aw.compact = compact
216
Nigel Tao4ede8452011-04-28 11:27:25 +1000217 v := reflect.ValueOf(pb)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700218 // We should normally be passed a struct, or a pointer to a struct,
219 // and we don't want the outer < and > in that case.
220 v = reflect.Indirect(v)
David Symondsa9cda212011-04-15 01:23:17 -0700221 if v.Kind() == reflect.Struct {
222 writeStruct(aw, v)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700223 } else {
David Symonds9f402812011-04-28 18:08:44 +1000224 writeAny(aw, v, nil)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700225 }
226}
227
228// MarshalText writes a given protobuffer in Text format.
229// Non-protobuffers can also be written, but their formatting is not guaranteed.
230func MarshalText(w io.Writer, pb interface{}) { marshalText(w, pb, false) }
231
232// CompactText writes a given protobuffer in compact Text format (one line).
233// Non-protobuffers can also be written, but their formatting is not guaranteed.
234func CompactText(w io.Writer, pb interface{}) { marshalText(w, pb, true) }
235
236// CompactTextString is the same as CompactText, but returns the string directly.
237func CompactTextString(pb interface{}) string {
238 buf := new(bytes.Buffer)
239 marshalText(buf, pb, true)
240 return buf.String()
241}