github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/encoding/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  	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  }