blob: ee80c85c795e46c4b11ff1ff59df9a46c7394c82 [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.
35// TODO:
36// - groups.
37
38import (
39 "bytes"
40 "fmt"
41 "io"
42 "os"
43 "reflect"
44 "strconv"
45 "strings"
46)
47
48// An io.Writer wrapper that tracks its indentation level.
49type textWriter struct {
50 indent_level 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
54}
55
56func (w *textWriter) Write(p []byte) (n int, err os.Error) {
57 n, err = len(p), nil
58
Rob Pike53385442010-06-30 22:22:43 -070059 frags := strings.Split(string(p), "\n", -1)
Rob Pikeaaa3a622010-03-20 22:32:34 -070060 if w.compact {
61 w.writer.Write([]byte(strings.Join(frags, " ")))
62 return
63 }
64
65 for i := 0; i < len(frags); i++ {
66 if w.complete {
67 for j := 0; j < w.indent_level; j++ {
68 w.writer.Write([]byte{' ', ' '})
69 }
70 w.complete = false
71 }
72
73 w.writer.Write([]byte(frags[i]))
74 if i+1 < len(frags) {
75 w.writer.Write([]byte{'\n'})
76 }
77 }
78 w.complete = len(frags[len(frags)-1]) == 0
79
80 return
81}
82
83func (w *textWriter) indent() { w.indent_level++ }
84
85func (w *textWriter) unindent() {
86 if w.indent_level == 0 {
87 fmt.Fprintln(os.Stderr, "proto: textWriter unindented too far!")
88 } else {
89 w.indent_level--
90 }
91}
92
93func writeStruct(w *textWriter, sv *reflect.StructValue) {
94 st := sv.Type().(*reflect.StructType)
95 sprops := GetProperties(st)
96 for i := 0; i < sv.NumField(); i++ {
97 if strings.HasPrefix(st.Field(i).Name, "XXX_") {
98 continue
99 }
100 props := sprops.Prop[i]
101 fv := sv.Field(i)
102 if pv, ok := fv.(*reflect.PtrValue); ok && pv.IsNil() {
103 // Field not filled in. This could be an optional field or
104 // a required field that wasn't filled in. Either way, there
105 // isn't anything we can show for it.
106 continue
107 }
108 if av, ok := fv.(*reflect.SliceValue); ok && av.IsNil() {
109 // Repeated field that is empty, or a bytes field that is unused.
110 continue
111 }
112
113 if props.Repeated {
114 if av, ok := fv.(*reflect.SliceValue); ok {
115 // Repeated field.
116 for j := 0; j < av.Len(); j++ {
117 fmt.Fprintf(w, "%v:", props.OrigName)
118 if !w.compact {
119 w.Write([]byte{' '})
120 }
121 writeAny(w, av.Elem(j))
122 fmt.Fprint(w, "\n")
123 }
124 continue
125 }
126 }
127
128 fmt.Fprintf(w, "%v:", props.OrigName)
129 if !w.compact {
130 w.Write([]byte{' '})
131 }
132 if len(props.Enum) == 0 || !tryWriteEnum(w, props.Enum, fv) {
133 writeAny(w, fv)
134 }
135 fmt.Fprint(w, "\n")
136 }
137}
138
139func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool {
Rob Pikeab5b8022010-06-21 17:47:58 -0700140 v = reflect.Indirect(v)
141 if v.Type().Kind() != reflect.Int32 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700142 return false
143 }
144 m, ok := enumNameMaps[enum]
145 if !ok {
146 return false
147 }
Rob Pikeab5b8022010-06-21 17:47:58 -0700148 str, ok := m[int32(v.(*reflect.IntValue).Get())]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700149 if !ok {
150 return false
151 }
152 fmt.Fprintf(w, str)
153 return true
154}
155
156func writeAny(w *textWriter, v reflect.Value) {
157 v = reflect.Indirect(v)
158
159 // We don't attempt to serialise every possible value type; only those
160 // that can occur in protocol buffers, plus a few extra that were easy.
161 switch val := v.(type) {
162 case *reflect.SliceValue:
163 // Should only be a []byte; repeated fields are handled in writeStruct.
Rob Pikeab5b8022010-06-21 17:47:58 -0700164 // TODO: Handle other cases more cleanly.
Rob Pikeaaa3a622010-03-20 22:32:34 -0700165 bytes := make([]byte, val.Len())
166 for i := 0; i < val.Len(); i++ {
Rob Pikeab5b8022010-06-21 17:47:58 -0700167 bytes[i] = byte(val.Elem(i).(*reflect.UintValue).Get())
Rob Pikeaaa3a622010-03-20 22:32:34 -0700168 }
169 // TODO: Should be strconv.QuoteC, which doesn't exist yet
170 fmt.Fprint(w, strconv.Quote(string(bytes)))
171 case *reflect.StringValue:
172 // TODO: Should be strconv.QuoteC, which doesn't exist yet
173 fmt.Fprint(w, strconv.Quote(val.Get()))
174 case *reflect.StructValue:
175 // Required/optional group/message.
176 // TODO: groups use { } instead of < >, and no colon.
177 if !w.compact {
178 fmt.Fprint(w, "<\n")
179 } else {
180 fmt.Fprint(w, "<")
181 }
182 w.indent()
183 writeStruct(w, val)
184 w.unindent()
185 fmt.Fprint(w, ">")
186 default:
187 fmt.Fprint(w, val.Interface())
188 }
189}
190
191func marshalText(w io.Writer, pb interface{}, compact bool) {
David Symonds03c9d412010-08-26 14:23:18 +1000192 if pb == nil {
193 w.Write([]byte("<nil>"))
194 return
195 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700196 aw := new(textWriter)
197 aw.writer = w
198 aw.complete = true
199 aw.compact = compact
200
201 v := reflect.NewValue(pb)
202 // We should normally be passed a struct, or a pointer to a struct,
203 // and we don't want the outer < and > in that case.
204 v = reflect.Indirect(v)
205 if sv, ok := v.(*reflect.StructValue); ok {
206 writeStruct(aw, sv)
207 } else {
208 writeAny(aw, v)
209 }
210}
211
212// MarshalText writes a given protobuffer in Text format.
213// Non-protobuffers can also be written, but their formatting is not guaranteed.
214func MarshalText(w io.Writer, pb interface{}) { marshalText(w, pb, false) }
215
216// CompactText writes a given protobuffer in compact Text format (one line).
217// Non-protobuffers can also be written, but their formatting is not guaranteed.
218func CompactText(w io.Writer, pb interface{}) { marshalText(w, pb, true) }
219
220// CompactTextString is the same as CompactText, but returns the string directly.
221func CompactTextString(pb interface{}) string {
222 buf := new(bytes.Buffer)
223 marshalText(buf, pb, true)
224 return buf.String()
225}