...
Run Format

Source file src/encoding/xml/typeinfo.go

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

View as plain text