blob: bb32983a88f621cface6d43c8c9a00a3b9cda981 [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
David Symondse37856c2011-06-22 12:52:53 +100034// Functions for writing the text protocol buffer format.
35// TODO: message sets.
Rob Pikeaaa3a622010-03-20 22:32:34 -070036
37import (
38 "bytes"
39 "fmt"
40 "io"
David Symondse37856c2011-06-22 12:52:53 +100041 "log"
Rob Pikeaaa3a622010-03-20 22:32:34 -070042 "os"
43 "reflect"
44 "strconv"
45 "strings"
46)
47
David Symondse37856c2011-06-22 12:52:53 +100048// textWriter is an io.Writer that tracks its indentation level.
Rob Pikeaaa3a622010-03-20 22:32:34 -070049type textWriter struct {
David Symondse37856c2011-06-22 12:52:53 +100050 ind int
51 complete bool // if the current position is a complete line
52 compact bool // whether to write out as a one-liner
53 writer io.Writer
David Symonds9f402812011-04-28 18:08:44 +100054
55 c [1]byte // scratch
Rob Pikeaaa3a622010-03-20 22:32:34 -070056}
57
58func (w *textWriter) Write(p []byte) (n int, err os.Error) {
59 n, err = len(p), nil
60
Rob Pike53385442010-06-30 22:22:43 -070061 frags := strings.Split(string(p), "\n", -1)
Rob Pikeaaa3a622010-03-20 22:32:34 -070062 if w.compact {
63 w.writer.Write([]byte(strings.Join(frags, " ")))
64 return
65 }
66
David Symondse37856c2011-06-22 12:52:53 +100067 for i, frag := range frags {
Rob Pikeaaa3a622010-03-20 22:32:34 -070068 if w.complete {
David Symondse37856c2011-06-22 12:52:53 +100069 for j := 0; j < w.ind; j++ {
Rob Pikeaaa3a622010-03-20 22:32:34 -070070 w.writer.Write([]byte{' ', ' '})
71 }
72 w.complete = false
73 }
74
David Symondse37856c2011-06-22 12:52:53 +100075 w.writer.Write([]byte(frag))
Rob Pikeaaa3a622010-03-20 22:32:34 -070076 if i+1 < len(frags) {
77 w.writer.Write([]byte{'\n'})
78 }
79 }
80 w.complete = len(frags[len(frags)-1]) == 0
81
82 return
83}
84
David Symonds9f402812011-04-28 18:08:44 +100085func (w *textWriter) WriteByte(c byte) os.Error {
86 w.c[0] = c
87 _, err := w.Write(w.c[:])
88 return err
89}
90
David Symondse37856c2011-06-22 12:52:53 +100091func (w *textWriter) indent() { w.ind++ }
Rob Pikeaaa3a622010-03-20 22:32:34 -070092
93func (w *textWriter) unindent() {
David Symondse37856c2011-06-22 12:52:53 +100094 if w.ind == 0 {
95 log.Printf("proto: textWriter unindented too far!")
96 return
Rob Pikeaaa3a622010-03-20 22:32:34 -070097 }
David Symondse37856c2011-06-22 12:52:53 +100098 w.ind--
Rob Pikeaaa3a622010-03-20 22:32:34 -070099}
100
David Symonds9f402812011-04-28 18:08:44 +1000101func writeName(w *textWriter, props *Properties) {
102 io.WriteString(w, props.OrigName)
103 if props.Wire != "group" {
104 w.WriteByte(':')
105 }
106}
107
David Symondse37856c2011-06-22 12:52:53 +1000108var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
109
Rob Pike97e934d2011-04-11 12:52:49 -0700110func writeStruct(w *textWriter, sv reflect.Value) {
111 st := sv.Type()
Rob Pikeaaa3a622010-03-20 22:32:34 -0700112 sprops := GetProperties(st)
113 for i := 0; i < sv.NumField(); i++ {
114 if strings.HasPrefix(st.Field(i).Name, "XXX_") {
115 continue
116 }
117 props := sprops.Prop[i]
118 fv := sv.Field(i)
Rob Pikeac8b1ce2011-04-11 16:14:54 -0700119 if fv.Kind() == reflect.Ptr && fv.IsNil() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700120 // Field not filled in. This could be an optional field or
121 // a required field that wasn't filled in. Either way, there
122 // isn't anything we can show for it.
123 continue
124 }
Rob Pikeac8b1ce2011-04-11 16:14:54 -0700125 if fv.Kind() == reflect.Slice && fv.IsNil() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700126 // Repeated field that is empty, or a bytes field that is unused.
127 continue
128 }
129
130 if props.Repeated {
David Symondse37856c2011-06-22 12:52:53 +1000131 if fv.Kind() == reflect.Slice {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700132 // Repeated field.
David Symondse37856c2011-06-22 12:52:53 +1000133 for j := 0; j < fv.Len(); j++ {
David Symonds9f402812011-04-28 18:08:44 +1000134 writeName(w, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700135 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000136 w.WriteByte(' ')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700137 }
David Symondse37856c2011-06-22 12:52:53 +1000138 writeAny(w, fv.Index(j), props)
139 w.WriteByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700140 }
141 continue
142 }
143 }
144
David Symonds9f402812011-04-28 18:08:44 +1000145 writeName(w, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700146 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000147 w.WriteByte(' ')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700148 }
David Symondse37856c2011-06-22 12:52:53 +1000149 if props.Enum != "" && tryWriteEnum(w, props.Enum, fv) {
150 // Enum written.
151 } else {
David Symonds9f402812011-04-28 18:08:44 +1000152 writeAny(w, fv, props)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700153 }
David Symondse37856c2011-06-22 12:52:53 +1000154 w.WriteByte('\n')
155 }
156
157 // Extensions.
158 pv := sv.Addr()
159 if pv.Type().Implements(extendableProtoType) {
160 writeExtensions(w, pv)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700161 }
162}
163
David Symondse37856c2011-06-22 12:52:53 +1000164// tryWriteEnum attempts to write an enum value as a symbolic constant.
165// If the enum is unregistered, nothing is written and false is returned.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700166func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool {
Rob Pikeab5b8022010-06-21 17:47:58 -0700167 v = reflect.Indirect(v)
168 if v.Type().Kind() != reflect.Int32 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700169 return false
170 }
171 m, ok := enumNameMaps[enum]
172 if !ok {
173 return false
174 }
Rob Pike97e934d2011-04-11 12:52:49 -0700175 str, ok := m[int32(v.Int())]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700176 if !ok {
177 return false
178 }
179 fmt.Fprintf(w, str)
180 return true
181}
182
David Symondse37856c2011-06-22 12:52:53 +1000183// writeAny writes an arbitrary field.
David Symonds9f402812011-04-28 18:08:44 +1000184func writeAny(w *textWriter, v reflect.Value, props *Properties) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700185 v = reflect.Indirect(v)
186
187 // We don't attempt to serialise every possible value type; only those
188 // that can occur in protocol buffers, plus a few extra that were easy.
David Symondse37856c2011-06-22 12:52:53 +1000189 switch v.Kind() {
Rob Pike97e934d2011-04-11 12:52:49 -0700190 case reflect.Slice:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700191 // Should only be a []byte; repeated fields are handled in writeStruct.
David Symondse37856c2011-06-22 12:52:53 +1000192 // TODO: Should be strconv.QuoteToASCII, which should be released after 2011-06-20.
193 fmt.Fprint(w, strconv.Quote(string(v.Interface().([]byte))))
Rob Pike97e934d2011-04-11 12:52:49 -0700194 case reflect.String:
David Symondse37856c2011-06-22 12:52:53 +1000195 // TODO: Should be strconv.QuoteToASCII, which should be released after 2011-06-20.
196 fmt.Fprint(w, strconv.Quote(v.String()))
Rob Pike97e934d2011-04-11 12:52:49 -0700197 case reflect.Struct:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700198 // Required/optional group/message.
David Symonds9f402812011-04-28 18:08:44 +1000199 var bra, ket byte = '<', '>'
200 if props != nil && props.Wire == "group" {
201 bra, ket = '{', '}'
202 }
203 w.WriteByte(bra)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700204 if !w.compact {
David Symonds9f402812011-04-28 18:08:44 +1000205 w.WriteByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700206 }
207 w.indent()
David Symondse37856c2011-06-22 12:52:53 +1000208 writeStruct(w, v)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700209 w.unindent()
David Symonds9f402812011-04-28 18:08:44 +1000210 w.WriteByte(ket)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700211 default:
David Symondse37856c2011-06-22 12:52:53 +1000212 fmt.Fprint(w, v.Interface())
213 }
214}
215
216// writeExtensions writes all the extensions in pv.
217// pv is assumed to be a pointer to a protocol message struct that is extendable.
218func writeExtensions(w *textWriter, pv reflect.Value) {
219 emap := extensionMaps[pv.Type().Elem()]
220 ep := pv.Interface().(extendableProto)
221 for extNum := range ep.ExtensionMap() {
222 var desc *ExtensionDesc
223 if emap != nil {
224 desc = emap[extNum]
225 }
226 if desc == nil {
227 // TODO: Handle printing unknown extensions.
228 fmt.Fprintln(os.Stderr, "proto: unknown extension: ", extNum)
229 continue
230 }
231
232 pb, err := GetExtension(ep, desc)
233 if err != nil {
234 fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err)
235 continue
236 }
237
238 fmt.Fprintf(w, "[%s]:", desc.Name)
239 if !w.compact {
240 w.WriteByte(' ')
241 }
242 writeAny(w, reflect.ValueOf(pb), nil)
243 w.WriteByte('\n')
Rob Pikeaaa3a622010-03-20 22:32:34 -0700244 }
245}
246
247func marshalText(w io.Writer, pb interface{}, compact bool) {
David Symonds03c9d412010-08-26 14:23:18 +1000248 if pb == nil {
249 w.Write([]byte("<nil>"))
250 return
251 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700252 aw := new(textWriter)
253 aw.writer = w
254 aw.complete = true
255 aw.compact = compact
256
Nigel Tao4ede8452011-04-28 11:27:25 +1000257 v := reflect.ValueOf(pb)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700258 // We should normally be passed a struct, or a pointer to a struct,
259 // and we don't want the outer < and > in that case.
260 v = reflect.Indirect(v)
David Symondsa9cda212011-04-15 01:23:17 -0700261 if v.Kind() == reflect.Struct {
262 writeStruct(aw, v)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700263 } else {
David Symonds9f402812011-04-28 18:08:44 +1000264 writeAny(aw, v, nil)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700265 }
266}
267
David Symondse37856c2011-06-22 12:52:53 +1000268// MarshalText writes a given protocol buffer in text format.
269// Values that are not protocol buffers can also be written, but their formatting is not guaranteed.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700270func MarshalText(w io.Writer, pb interface{}) { marshalText(w, pb, false) }
271
David Symondse37856c2011-06-22 12:52:53 +1000272// CompactText writes a given protocl buffer in compact text format (one line).
273// Values that are not protocol buffers can also be written, but their formatting is not guaranteed.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700274func CompactText(w io.Writer, pb interface{}) { marshalText(w, pb, true) }
275
276// CompactTextString is the same as CompactText, but returns the string directly.
277func CompactTextString(pb interface{}) string {
278 buf := new(bytes.Buffer)
279 marshalText(buf, pb, true)
280 return buf.String()
281}