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