properties.go raw

   1  // Copyright 2010 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package proto
   6  
   7  import (
   8  	"fmt"
   9  	"reflect"
  10  	"strconv"
  11  	"strings"
  12  	"sync"
  13  
  14  	"google.golang.org/protobuf/reflect/protoreflect"
  15  	"google.golang.org/protobuf/runtime/protoimpl"
  16  )
  17  
  18  // StructProperties represents protocol buffer type information for a
  19  // generated protobuf message in the open-struct API.
  20  //
  21  // Deprecated: Do not use.
  22  type StructProperties struct {
  23  	// Prop are the properties for each field.
  24  	//
  25  	// Fields belonging to a oneof are stored in OneofTypes instead, with a
  26  	// single Properties representing the parent oneof held here.
  27  	//
  28  	// The order of Prop matches the order of fields in the Go struct.
  29  	// Struct fields that are not related to protobufs have a "XXX_" prefix
  30  	// in the Properties.Name and must be ignored by the user.
  31  	Prop []*Properties
  32  
  33  	// OneofTypes contains information about the oneof fields in this message.
  34  	// It is keyed by the protobuf field name.
  35  	OneofTypes map[string]*OneofProperties
  36  }
  37  
  38  // Properties represents the type information for a protobuf message field.
  39  //
  40  // Deprecated: Do not use.
  41  type Properties struct {
  42  	// Name is a placeholder name with little meaningful semantic value.
  43  	// If the name has an "XXX_" prefix, the entire Properties must be ignored.
  44  	Name string
  45  	// OrigName is the protobuf field name or oneof name.
  46  	OrigName string
  47  	// JSONName is the JSON name for the protobuf field.
  48  	JSONName string
  49  	// Enum is a placeholder name for enums.
  50  	// For historical reasons, this is neither the Go name for the enum,
  51  	// nor the protobuf name for the enum.
  52  	Enum string // Deprecated: Do not use.
  53  	// Weak contains the full name of the weakly referenced message.
  54  	Weak string
  55  	// Wire is a string representation of the wire type.
  56  	Wire string
  57  	// WireType is the protobuf wire type for the field.
  58  	WireType int
  59  	// Tag is the protobuf field number.
  60  	Tag int
  61  	// Required reports whether this is a required field.
  62  	Required bool
  63  	// Optional reports whether this is a optional field.
  64  	Optional bool
  65  	// Repeated reports whether this is a repeated field.
  66  	Repeated bool
  67  	// Packed reports whether this is a packed repeated field of scalars.
  68  	Packed bool
  69  	// Proto3 reports whether this field operates under the proto3 syntax.
  70  	Proto3 bool
  71  	// Oneof reports whether this field belongs within a oneof.
  72  	Oneof bool
  73  
  74  	// Default is the default value in string form.
  75  	Default string
  76  	// HasDefault reports whether the field has a default value.
  77  	HasDefault bool
  78  
  79  	// MapKeyProp is the properties for the key field for a map field.
  80  	MapKeyProp *Properties
  81  	// MapValProp is the properties for the value field for a map field.
  82  	MapValProp *Properties
  83  }
  84  
  85  // OneofProperties represents the type information for a protobuf oneof.
  86  //
  87  // Deprecated: Do not use.
  88  type OneofProperties struct {
  89  	// Type is a pointer to the generated wrapper type for the field value.
  90  	// This is nil for messages that are not in the open-struct API.
  91  	Type reflect.Type
  92  	// Field is the index into StructProperties.Prop for the containing oneof.
  93  	Field int
  94  	// Prop is the properties for the field.
  95  	Prop *Properties
  96  }
  97  
  98  // String formats the properties in the protobuf struct field tag style.
  99  func (p *Properties) String() string {
 100  	s := p.Wire
 101  	s += "," + strconv.Itoa(p.Tag)
 102  	if p.Required {
 103  		s += ",req"
 104  	}
 105  	if p.Optional {
 106  		s += ",opt"
 107  	}
 108  	if p.Repeated {
 109  		s += ",rep"
 110  	}
 111  	if p.Packed {
 112  		s += ",packed"
 113  	}
 114  	s += ",name=" + p.OrigName
 115  	if p.JSONName != "" {
 116  		s += ",json=" + p.JSONName
 117  	}
 118  	if len(p.Enum) > 0 {
 119  		s += ",enum=" + p.Enum
 120  	}
 121  	if len(p.Weak) > 0 {
 122  		s += ",weak=" + p.Weak
 123  	}
 124  	if p.Proto3 {
 125  		s += ",proto3"
 126  	}
 127  	if p.Oneof {
 128  		s += ",oneof"
 129  	}
 130  	if p.HasDefault {
 131  		s += ",def=" + p.Default
 132  	}
 133  	return s
 134  }
 135  
 136  // Parse populates p by parsing a string in the protobuf struct field tag style.
 137  func (p *Properties) Parse(tag string) {
 138  	// For example: "bytes,49,opt,name=foo,def=hello!"
 139  	for len(tag) > 0 {
 140  		i := strings.IndexByte(tag, ',')
 141  		if i < 0 {
 142  			i = len(tag)
 143  		}
 144  		switch s := tag[:i]; {
 145  		case strings.HasPrefix(s, "name="):
 146  			p.OrigName = s[len("name="):]
 147  		case strings.HasPrefix(s, "json="):
 148  			p.JSONName = s[len("json="):]
 149  		case strings.HasPrefix(s, "enum="):
 150  			p.Enum = s[len("enum="):]
 151  		case strings.HasPrefix(s, "weak="):
 152  			p.Weak = s[len("weak="):]
 153  		case strings.Trim(s, "0123456789") == "":
 154  			n, _ := strconv.ParseUint(s, 10, 32)
 155  			p.Tag = int(n)
 156  		case s == "opt":
 157  			p.Optional = true
 158  		case s == "req":
 159  			p.Required = true
 160  		case s == "rep":
 161  			p.Repeated = true
 162  		case s == "varint" || s == "zigzag32" || s == "zigzag64":
 163  			p.Wire = s
 164  			p.WireType = WireVarint
 165  		case s == "fixed32":
 166  			p.Wire = s
 167  			p.WireType = WireFixed32
 168  		case s == "fixed64":
 169  			p.Wire = s
 170  			p.WireType = WireFixed64
 171  		case s == "bytes":
 172  			p.Wire = s
 173  			p.WireType = WireBytes
 174  		case s == "group":
 175  			p.Wire = s
 176  			p.WireType = WireStartGroup
 177  		case s == "packed":
 178  			p.Packed = true
 179  		case s == "proto3":
 180  			p.Proto3 = true
 181  		case s == "oneof":
 182  			p.Oneof = true
 183  		case strings.HasPrefix(s, "def="):
 184  			// The default tag is special in that everything afterwards is the
 185  			// default regardless of the presence of commas.
 186  			p.HasDefault = true
 187  			p.Default, i = tag[len("def="):], len(tag)
 188  		}
 189  		tag = strings.TrimPrefix(tag[i:], ",")
 190  	}
 191  }
 192  
 193  // Init populates the properties from a protocol buffer struct tag.
 194  //
 195  // Deprecated: Do not use.
 196  func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
 197  	p.Name = name
 198  	p.OrigName = name
 199  	if tag == "" {
 200  		return
 201  	}
 202  	p.Parse(tag)
 203  
 204  	if typ != nil && typ.Kind() == reflect.Map {
 205  		p.MapKeyProp = new(Properties)
 206  		p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
 207  		p.MapValProp = new(Properties)
 208  		p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
 209  	}
 210  }
 211  
 212  var propertiesCache sync.Map // map[reflect.Type]*StructProperties
 213  
 214  // GetProperties returns the list of properties for the type represented by t,
 215  // which must be a generated protocol buffer message in the open-struct API,
 216  // where protobuf message fields are represented by exported Go struct fields.
 217  //
 218  // Deprecated: Use protobuf reflection instead.
 219  func GetProperties(t reflect.Type) *StructProperties {
 220  	if p, ok := propertiesCache.Load(t); ok {
 221  		return p.(*StructProperties)
 222  	}
 223  	p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
 224  	return p.(*StructProperties)
 225  }
 226  
 227  func newProperties(t reflect.Type) *StructProperties {
 228  	if t.Kind() != reflect.Struct {
 229  		panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
 230  	}
 231  
 232  	var hasOneof bool
 233  	prop := new(StructProperties)
 234  
 235  	// Construct a list of properties for each field in the struct.
 236  	for i := 0; i < t.NumField(); i++ {
 237  		p := new(Properties)
 238  		f := t.Field(i)
 239  		tagField := f.Tag.Get("protobuf")
 240  		p.Init(f.Type, f.Name, tagField, &f)
 241  
 242  		tagOneof := f.Tag.Get("protobuf_oneof")
 243  		if tagOneof != "" {
 244  			hasOneof = true
 245  			p.OrigName = tagOneof
 246  		}
 247  
 248  		// Rename unrelated struct fields with the "XXX_" prefix since so much
 249  		// user code simply checks for this to exclude special fields.
 250  		if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
 251  			p.Name = "XXX_" + p.Name
 252  			p.OrigName = "XXX_" + p.OrigName
 253  		} else if p.Weak != "" {
 254  			p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
 255  		}
 256  
 257  		prop.Prop = append(prop.Prop, p)
 258  	}
 259  
 260  	// Construct a mapping of oneof field names to properties.
 261  	if hasOneof {
 262  		var oneofWrappers []interface{}
 263  		if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
 264  			oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
 265  		}
 266  		if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
 267  			oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
 268  		}
 269  		if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
 270  			if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
 271  				oneofWrappers = m.ProtoMessageInfo().OneofWrappers
 272  			}
 273  		}
 274  
 275  		prop.OneofTypes = make(map[string]*OneofProperties)
 276  		for _, wrapper := range oneofWrappers {
 277  			p := &OneofProperties{
 278  				Type: reflect.ValueOf(wrapper).Type(), // *T
 279  				Prop: new(Properties),
 280  			}
 281  			f := p.Type.Elem().Field(0)
 282  			p.Prop.Name = f.Name
 283  			p.Prop.Parse(f.Tag.Get("protobuf"))
 284  
 285  			// Determine the struct field that contains this oneof.
 286  			// Each wrapper is assignable to exactly one parent field.
 287  			var foundOneof bool
 288  			for i := 0; i < t.NumField() && !foundOneof; i++ {
 289  				if p.Type.AssignableTo(t.Field(i).Type) {
 290  					p.Field = i
 291  					foundOneof = true
 292  				}
 293  			}
 294  			if !foundOneof {
 295  				panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
 296  			}
 297  			prop.OneofTypes[p.Prop.OrigName] = p
 298  		}
 299  	}
 300  
 301  	return prop
 302  }
 303  
 304  func (sp *StructProperties) Len() int           { return len(sp.Prop) }
 305  func (sp *StructProperties) Less(i, j int) bool { return false }
 306  func (sp *StructProperties) Swap(i, j int)      { return }
 307