Run Format

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

View as plain text