github.com/vmware/govmomi@v0.37.2/vim25/xml/typeinfo.go (about)

     1  // Copyright 2011 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 xml
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  // typeInfo holds details for the xml representation of a type.
    15  type typeInfo struct {
    16  	xmlname *fieldInfo
    17  	fields  []fieldInfo
    18  }
    19  
    20  // fieldInfo holds details for the xml representation of a single field.
    21  type fieldInfo struct {
    22  	idx     []int
    23  	name    string
    24  	xmlns   string
    25  	flags   fieldFlags
    26  	parents []string
    27  }
    28  
    29  type fieldFlags int
    30  
    31  const (
    32  	fElement fieldFlags = 1 << iota
    33  	fAttr
    34  	fCDATA
    35  	fCharData
    36  	fInnerXML
    37  	fComment
    38  	fAny
    39  
    40  	fOmitEmpty
    41  	fTypeAttr
    42  
    43  	fMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny
    44  
    45  	xmlName = "XMLName"
    46  )
    47  
    48  var tinfoMap sync.Map // map[reflect.Type]*typeInfo
    49  
    50  var nameType = reflect.TypeOf(Name{})
    51  
    52  // getTypeInfo returns the typeInfo structure with details necessary
    53  // for marshaling and unmarshaling typ.
    54  func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
    55  	if ti, ok := tinfoMap.Load(typ); ok {
    56  		return ti.(*typeInfo), nil
    57  	}
    58  
    59  	tinfo := &typeInfo{}
    60  	if typ.Kind() == reflect.Struct && typ != nameType {
    61  		n := typ.NumField()
    62  		for i := 0; i < n; i++ {
    63  			f := typ.Field(i)
    64  			if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get("xml") == "-" {
    65  				continue // Private field
    66  			}
    67  
    68  			// For embedded structs, embed its fields.
    69  			if f.Anonymous {
    70  				t := f.Type
    71  				if t.Kind() == reflect.Ptr {
    72  					t = t.Elem()
    73  				}
    74  				if t.Kind() == reflect.Struct {
    75  					inner, err := getTypeInfo(t)
    76  					if err != nil {
    77  						return nil, err
    78  					}
    79  					if tinfo.xmlname == nil {
    80  						tinfo.xmlname = inner.xmlname
    81  					}
    82  					for _, finfo := range inner.fields {
    83  						finfo.idx = append([]int{i}, finfo.idx...)
    84  						if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
    85  							return nil, err
    86  						}
    87  					}
    88  					continue
    89  				}
    90  			}
    91  
    92  			finfo, err := structFieldInfo(typ, &f)
    93  			if err != nil {
    94  				return nil, err
    95  			}
    96  
    97  			if f.Name == xmlName {
    98  				tinfo.xmlname = finfo
    99  				continue
   100  			}
   101  
   102  			// Add the field if it doesn't conflict with other fields.
   103  			if err := addFieldInfo(typ, tinfo, finfo); err != nil {
   104  				return nil, err
   105  			}
   106  		}
   107  	}
   108  
   109  	ti, _ := tinfoMap.LoadOrStore(typ, tinfo)
   110  	return ti.(*typeInfo), nil
   111  }
   112  
   113  // structFieldInfo builds and returns a fieldInfo for f.
   114  func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
   115  	finfo := &fieldInfo{idx: f.Index}
   116  
   117  	// Split the tag from the xml namespace if necessary.
   118  	tag := f.Tag.Get("xml")
   119  	if i := strings.Index(tag, " "); i >= 0 {
   120  		finfo.xmlns, tag = tag[:i], tag[i+1:]
   121  	}
   122  
   123  	// Parse flags.
   124  	tokens := strings.Split(tag, ",")
   125  	if len(tokens) == 1 {
   126  		finfo.flags = fElement
   127  	} else {
   128  		tag = tokens[0]
   129  		for _, flag := range tokens[1:] {
   130  			switch flag {
   131  			case "attr":
   132  				finfo.flags |= fAttr
   133  			case "cdata":
   134  				finfo.flags |= fCDATA
   135  			case "chardata":
   136  				finfo.flags |= fCharData
   137  			case "innerxml":
   138  				finfo.flags |= fInnerXML
   139  			case "comment":
   140  				finfo.flags |= fComment
   141  			case "any":
   142  				finfo.flags |= fAny
   143  			case "omitempty":
   144  				finfo.flags |= fOmitEmpty
   145  			case "typeattr":
   146  				finfo.flags |= fTypeAttr
   147  			}
   148  		}
   149  
   150  		// Validate the flags used.
   151  		valid := true
   152  		switch mode := finfo.flags & fMode; mode {
   153  		case 0:
   154  			finfo.flags |= fElement
   155  		case fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:
   156  			if f.Name == xmlName || tag != "" && mode != fAttr {
   157  				valid = false
   158  			}
   159  		default:
   160  			// This will also catch multiple modes in a single field.
   161  			valid = false
   162  		}
   163  		if finfo.flags&fMode == fAny {
   164  			finfo.flags |= fElement
   165  		}
   166  		if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
   167  			valid = false
   168  		}
   169  		if !valid {
   170  			return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
   171  				f.Name, typ, f.Tag.Get("xml"))
   172  		}
   173  	}
   174  
   175  	// Use of xmlns without a name is not allowed.
   176  	if finfo.xmlns != "" && tag == "" {
   177  		return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
   178  			f.Name, typ, f.Tag.Get("xml"))
   179  	}
   180  
   181  	if f.Name == xmlName {
   182  		// The XMLName field records the XML element name. Don't
   183  		// process it as usual because its name should default to
   184  		// empty rather than to the field name.
   185  		finfo.name = tag
   186  		return finfo, nil
   187  	}
   188  
   189  	if tag == "" {
   190  		// If the name part of the tag is completely empty, get
   191  		// default from XMLName of underlying struct if feasible,
   192  		// or field name otherwise.
   193  		if xmlname := lookupXMLName(f.Type); xmlname != nil {
   194  			finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
   195  		} else {
   196  			finfo.name = f.Name
   197  		}
   198  		return finfo, nil
   199  	}
   200  
   201  	// Prepare field name and parents.
   202  	parents := strings.Split(tag, ">")
   203  	if parents[0] == "" {
   204  		parents[0] = f.Name
   205  	}
   206  	if parents[len(parents)-1] == "" {
   207  		return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
   208  	}
   209  	finfo.name = parents[len(parents)-1]
   210  	if len(parents) > 1 {
   211  		if (finfo.flags & fElement) == 0 {
   212  			return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
   213  		}
   214  		finfo.parents = parents[:len(parents)-1]
   215  	}
   216  
   217  	// If the field type has an XMLName field, the names must match
   218  	// so that the behavior of both marshaling and unmarshaling
   219  	// is straightforward and unambiguous.
   220  	if finfo.flags&fElement != 0 {
   221  		ftyp := f.Type
   222  		xmlname := lookupXMLName(ftyp)
   223  		if xmlname != nil && xmlname.name != finfo.name {
   224  			return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
   225  				finfo.name, typ, f.Name, xmlname.name, ftyp)
   226  		}
   227  	}
   228  	return finfo, nil
   229  }
   230  
   231  // lookupXMLName returns the fieldInfo for typ's XMLName field
   232  // in case it exists and has a valid xml field tag, otherwise
   233  // it returns nil.
   234  func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
   235  	for typ.Kind() == reflect.Ptr {
   236  		typ = typ.Elem()
   237  	}
   238  	if typ.Kind() != reflect.Struct {
   239  		return nil
   240  	}
   241  	for i, n := 0, typ.NumField(); i < n; i++ {
   242  		f := typ.Field(i)
   243  		if f.Name != xmlName {
   244  			continue
   245  		}
   246  		finfo, err := structFieldInfo(typ, &f)
   247  		if err == nil && finfo.name != "" {
   248  			return finfo
   249  		}
   250  		// Also consider errors as a non-existent field tag
   251  		// and let getTypeInfo itself report the error.
   252  		break
   253  	}
   254  	return nil
   255  }
   256  
   257  func min(a, b int) int {
   258  	if a <= b {
   259  		return a
   260  	}
   261  	return b
   262  }
   263  
   264  // addFieldInfo adds finfo to tinfo.fields if there are no
   265  // conflicts, or if conflicts arise from previous fields that were
   266  // obtained from deeper embedded structures than finfo. In the latter
   267  // case, the conflicting entries are dropped.
   268  // A conflict occurs when the path (parent + name) to a field is
   269  // itself a prefix of another path, or when two paths match exactly.
   270  // It is okay for field paths to share a common, shorter prefix.
   271  func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
   272  	var conflicts []int
   273  Loop:
   274  	// First, figure all conflicts. Most working code will have none.
   275  	for i := range tinfo.fields {
   276  		oldf := &tinfo.fields[i]
   277  		if oldf.flags&fMode != newf.flags&fMode {
   278  			continue
   279  		}
   280  		if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
   281  			continue
   282  		}
   283  		minl := min(len(newf.parents), len(oldf.parents))
   284  		for p := 0; p < minl; p++ {
   285  			if oldf.parents[p] != newf.parents[p] {
   286  				continue Loop
   287  			}
   288  		}
   289  		if len(oldf.parents) > len(newf.parents) {
   290  			if oldf.parents[len(newf.parents)] == newf.name {
   291  				conflicts = append(conflicts, i)
   292  			}
   293  		} else if len(oldf.parents) < len(newf.parents) {
   294  			if newf.parents[len(oldf.parents)] == oldf.name {
   295  				conflicts = append(conflicts, i)
   296  			}
   297  		} else {
   298  			if newf.name == oldf.name {
   299  				conflicts = append(conflicts, i)
   300  			}
   301  		}
   302  	}
   303  	// Without conflicts, add the new field and return.
   304  	if conflicts == nil {
   305  		tinfo.fields = append(tinfo.fields, *newf)
   306  		return nil
   307  	}
   308  
   309  	// If any conflict is shallower, ignore the new field.
   310  	// This matches the Go field resolution on embedding.
   311  	for _, i := range conflicts {
   312  		if len(tinfo.fields[i].idx) < len(newf.idx) {
   313  			return nil
   314  		}
   315  	}
   316  
   317  	// Otherwise, if any of them is at the same depth level, it's an error.
   318  	for _, i := range conflicts {
   319  		oldf := &tinfo.fields[i]
   320  		if len(oldf.idx) == len(newf.idx) {
   321  			f1 := typ.FieldByIndex(oldf.idx)
   322  			f2 := typ.FieldByIndex(newf.idx)
   323  			return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
   324  		}
   325  	}
   326  
   327  	// Otherwise, the new field is shallower, and thus takes precedence,
   328  	// so drop the conflicting fields from tinfo and append the new one.
   329  	for c := len(conflicts) - 1; c >= 0; c-- {
   330  		i := conflicts[c]
   331  		copy(tinfo.fields[i:], tinfo.fields[i+1:])
   332  		tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
   333  	}
   334  	tinfo.fields = append(tinfo.fields, *newf)
   335  	return nil
   336  }
   337  
   338  // A TagPathError represents an error in the unmarshaling process
   339  // caused by the use of field tags with conflicting paths.
   340  type TagPathError struct {
   341  	Struct       reflect.Type
   342  	Field1, Tag1 string
   343  	Field2, Tag2 string
   344  }
   345  
   346  func (e *TagPathError) Error() string {
   347  	return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
   348  }
   349  
   350  const (
   351  	initNilPointers     = true
   352  	dontInitNilPointers = false
   353  )
   354  
   355  // value returns v's field value corresponding to finfo.
   356  // It's equivalent to v.FieldByIndex(finfo.idx), but when passed
   357  // initNilPointers, it initializes and dereferences pointers as necessary.
   358  // When passed dontInitNilPointers and a nil pointer is reached, the function
   359  // returns a zero reflect.Value.
   360  func (finfo *fieldInfo) value(v reflect.Value, shouldInitNilPointers bool) reflect.Value {
   361  	for i, x := range finfo.idx {
   362  		if i > 0 {
   363  			t := v.Type()
   364  			if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
   365  				if v.IsNil() {
   366  					if !shouldInitNilPointers {
   367  						return reflect.Value{}
   368  					}
   369  					v.Set(reflect.New(v.Type().Elem()))
   370  				}
   371  				v = v.Elem()
   372  			}
   373  		}
   374  		v = v.Field(x)
   375  	}
   376  	return v
   377  }