goprotobuf: sync from Google internal version.

Interesting changes:
  - String method on protocol buffer message types
    that renders the message in compact text format.
  - Extension text formatting.

R=r
CC=golang-dev
http://codereview.appspot.com/4643050
diff --git a/proto/text.go b/proto/text.go
index 1ca46e8..bb32983 100644
--- a/proto/text.go
+++ b/proto/text.go
@@ -31,25 +31,26 @@
 
 package proto
 
-// Functions for writing the Text protocol buffer format.
-// TODO: message sets, extensions.
+// Functions for writing the text protocol buffer format.
+// TODO: message sets.
 
 import (
 	"bytes"
 	"fmt"
 	"io"
+	"log"
 	"os"
 	"reflect"
 	"strconv"
 	"strings"
 )
 
-// An io.Writer wrapper that tracks its indentation level.
+// textWriter is an io.Writer that tracks its indentation level.
 type textWriter struct {
-	indent_level int
-	complete     bool // if the current position is a complete line
-	compact      bool // whether to write out as a one-liner
-	writer       io.Writer
+	ind      int
+	complete bool // if the current position is a complete line
+	compact  bool // whether to write out as a one-liner
+	writer   io.Writer
 
 	c [1]byte // scratch
 }
@@ -63,15 +64,15 @@
 		return
 	}
 
-	for i := 0; i < len(frags); i++ {
+	for i, frag := range frags {
 		if w.complete {
-			for j := 0; j < w.indent_level; j++ {
+			for j := 0; j < w.ind; j++ {
 				w.writer.Write([]byte{' ', ' '})
 			}
 			w.complete = false
 		}
 
-		w.writer.Write([]byte(frags[i]))
+		w.writer.Write([]byte(frag))
 		if i+1 < len(frags) {
 			w.writer.Write([]byte{'\n'})
 		}
@@ -87,14 +88,14 @@
 	return err
 }
 
-func (w *textWriter) indent() { w.indent_level++ }
+func (w *textWriter) indent() { w.ind++ }
 
 func (w *textWriter) unindent() {
-	if w.indent_level == 0 {
-		fmt.Fprintln(os.Stderr, "proto: textWriter unindented too far!")
-	} else {
-		w.indent_level--
+	if w.ind == 0 {
+		log.Printf("proto: textWriter unindented too far!")
+		return
 	}
+	w.ind--
 }
 
 func writeName(w *textWriter, props *Properties) {
@@ -104,6 +105,8 @@
 	}
 }
 
+var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
+
 func writeStruct(w *textWriter, sv reflect.Value) {
 	st := sv.Type()
 	sprops := GetProperties(st)
@@ -125,15 +128,15 @@
 		}
 
 		if props.Repeated {
-			if av := fv; av.Kind() == reflect.Slice {
+			if fv.Kind() == reflect.Slice {
 				// Repeated field.
-				for j := 0; j < av.Len(); j++ {
+				for j := 0; j < fv.Len(); j++ {
 					writeName(w, props)
 					if !w.compact {
 						w.WriteByte(' ')
 					}
-					writeAny(w, av.Index(j), props)
-					fmt.Fprint(w, "\n")
+					writeAny(w, fv.Index(j), props)
+					w.WriteByte('\n')
 				}
 				continue
 			}
@@ -143,13 +146,23 @@
 		if !w.compact {
 			w.WriteByte(' ')
 		}
-		if len(props.Enum) == 0 || !tryWriteEnum(w, props.Enum, fv) {
+		if props.Enum != "" && tryWriteEnum(w, props.Enum, fv) {
+			// Enum written.
+		} else {
 			writeAny(w, fv, props)
 		}
-		fmt.Fprint(w, "\n")
+		w.WriteByte('\n')
+	}
+
+	// Extensions.
+	pv := sv.Addr()
+	if pv.Type().Implements(extendableProtoType) {
+		writeExtensions(w, pv)
 	}
 }
 
+// tryWriteEnum attempts to write an enum value as a symbolic constant.
+// If the enum is unregistered, nothing is written and false is returned.
 func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool {
 	v = reflect.Indirect(v)
 	if v.Type().Kind() != reflect.Int32 {
@@ -167,24 +180,20 @@
 	return true
 }
 
+// writeAny writes an arbitrary field.
 func writeAny(w *textWriter, v reflect.Value, props *Properties) {
 	v = reflect.Indirect(v)
 
 	// We don't attempt to serialise every possible value type; only those
 	// that can occur in protocol buffers, plus a few extra that were easy.
-	switch val := v; val.Kind() {
+	switch v.Kind() {
 	case reflect.Slice:
 		// Should only be a []byte; repeated fields are handled in writeStruct.
-		// TODO: Handle other cases more cleanly.
-		bytes := make([]byte, val.Len())
-		for i := 0; i < val.Len(); i++ {
-			bytes[i] = byte(val.Index(i).Uint())
-		}
-		// TODO: Should be strconv.QuoteC, which doesn't exist yet
-		fmt.Fprint(w, strconv.Quote(string(bytes)))
+		// TODO: Should be strconv.QuoteToASCII, which should be released after 2011-06-20.
+		fmt.Fprint(w, strconv.Quote(string(v.Interface().([]byte))))
 	case reflect.String:
-		// TODO: Should be strconv.QuoteC, which doesn't exist yet
-		fmt.Fprint(w, strconv.Quote(val.String()))
+		// TODO: Should be strconv.QuoteToASCII, which should be released after 2011-06-20.
+		fmt.Fprint(w, strconv.Quote(v.String()))
 	case reflect.Struct:
 		// Required/optional group/message.
 		var bra, ket byte = '<', '>'
@@ -196,11 +205,42 @@
 			w.WriteByte('\n')
 		}
 		w.indent()
-		writeStruct(w, val)
+		writeStruct(w, v)
 		w.unindent()
 		w.WriteByte(ket)
 	default:
-		fmt.Fprint(w, val.Interface())
+		fmt.Fprint(w, v.Interface())
+	}
+}
+
+// writeExtensions writes all the extensions in pv.
+// pv is assumed to be a pointer to a protocol message struct that is extendable.
+func writeExtensions(w *textWriter, pv reflect.Value) {
+	emap := extensionMaps[pv.Type().Elem()]
+	ep := pv.Interface().(extendableProto)
+	for extNum := range ep.ExtensionMap() {
+		var desc *ExtensionDesc
+		if emap != nil {
+			desc = emap[extNum]
+		}
+		if desc == nil {
+			// TODO: Handle printing unknown extensions.
+			fmt.Fprintln(os.Stderr, "proto: unknown extension: ", extNum)
+			continue
+		}
+
+		pb, err := GetExtension(ep, desc)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err)
+			continue
+		}
+
+		fmt.Fprintf(w, "[%s]:", desc.Name)
+		if !w.compact {
+			w.WriteByte(' ')
+		}
+		writeAny(w, reflect.ValueOf(pb), nil)
+		w.WriteByte('\n')
 	}
 }
 
@@ -225,12 +265,12 @@
 	}
 }
 
-// MarshalText writes a given protobuffer in Text format.
-// Non-protobuffers can also be written, but their formatting is not guaranteed.
+// MarshalText writes a given protocol buffer in text format.
+// Values that are not protocol buffers can also be written, but their formatting is not guaranteed.
 func MarshalText(w io.Writer, pb interface{}) { marshalText(w, pb, false) }
 
-// CompactText writes a given protobuffer in compact Text format (one line).
-// Non-protobuffers can also be written, but their formatting is not guaranteed.
+// CompactText writes a given protocl buffer in compact text format (one line).
+// Values that are not protocol buffers can also be written, but their formatting is not guaranteed.
 func CompactText(w io.Writer, pb interface{}) { marshalText(w, pb, true) }
 
 // CompactTextString is the same as CompactText, but returns the string directly.