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	fCharData
35	fInnerXml
36	fComment
37	fAny
38
39	fOmitEmpty
40
41	fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
42)
43
44var tinfoMap = make(map[reflect.Type]*typeInfo)
45var tinfoLock sync.RWMutex
46
47var nameType = reflect.TypeOf(Name{})
48
49// getTypeInfo returns the typeInfo structure with details necessary
50// for marshalling and unmarshalling typ.
51func 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.
114func 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	if finfo.xmlns == "" && finfo.flags&fAttr == 0 {
198		// If it's an element no namespace specified, get the default
199		// from the XMLName of enclosing struct if possible.
200		if xmlname := lookupXMLName(typ); xmlname != nil {
201			finfo.xmlns = xmlname.xmlns
202		}
203	}
204
205	// Prepare field name and parents.
206	parents := strings.Split(tag, ">")
207	if parents[0] == "" {
208		parents[0] = f.Name
209	}
210	if parents[len(parents)-1] == "" {
211		return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
212	}
213	finfo.name = parents[len(parents)-1]
214	if len(parents) > 1 {
215		if (finfo.flags & fElement) == 0 {
216			return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
217		}
218		finfo.parents = parents[:len(parents)-1]
219	}
220
221	// If the field type has an XMLName field, the names must match
222	// so that the behavior of both marshalling and unmarshalling
223	// is straightforward and unambiguous.
224	if finfo.flags&fElement != 0 {
225		ftyp := f.Type
226		xmlname := lookupXMLName(ftyp)
227		if xmlname != nil && xmlname.name != finfo.name {
228			return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
229				finfo.name, typ, f.Name, xmlname.name, ftyp)
230		}
231	}
232	return finfo, nil
233}
234
235// lookupXMLName returns the fieldInfo for typ's XMLName field
236// in case it exists and has a valid xml field tag, otherwise
237// it returns nil.
238func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
239	for typ.Kind() == reflect.Ptr {
240		typ = typ.Elem()
241	}
242	if typ.Kind() != reflect.Struct {
243		return nil
244	}
245	for i, n := 0, typ.NumField(); i < n; i++ {
246		f := typ.Field(i)
247		if f.Name != "XMLName" {
248			continue
249		}
250		finfo, err := structFieldInfo(typ, &f)
251		if finfo.name != "" && err == nil {
252			return finfo
253		}
254		// Also consider errors as a non-existent field tag
255		// and let getTypeInfo itself report the error.
256		break
257	}
258	return nil
259}
260
261func min(a, b int) int {
262	if a <= b {
263		return a
264	}
265	return b
266}
267
268// addFieldInfo adds finfo to tinfo.fields if there are no
269// conflicts, or if conflicts arise from previous fields that were
270// obtained from deeper embedded structures than finfo. In the latter
271// case, the conflicting entries are dropped.
272// A conflict occurs when the path (parent + name) to a field is
273// itself a prefix of another path, or when two paths match exactly.
274// It is okay for field paths to share a common, shorter prefix.
275func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
276	var conflicts []int
277Loop:
278	// First, figure all conflicts. Most working code will have none.
279	for i := range tinfo.fields {
280		oldf := &tinfo.fields[i]
281		if oldf.flags&fMode != newf.flags&fMode {
282			continue
283		}
284		if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
285			continue
286		}
287		minl := min(len(newf.parents), len(oldf.parents))
288		for p := 0; p < minl; p++ {
289			if oldf.parents[p] != newf.parents[p] {
290				continue Loop
291			}
292		}
293		if len(oldf.parents) > len(newf.parents) {
294			if oldf.parents[len(newf.parents)] == newf.name {
295				conflicts = append(conflicts, i)
296			}
297		} else if len(oldf.parents) < len(newf.parents) {
298			if newf.parents[len(oldf.parents)] == oldf.name {
299				conflicts = append(conflicts, i)
300			}
301		} else {
302			if newf.name == oldf.name {
303				conflicts = append(conflicts, i)
304			}
305		}
306	}
307	// Without conflicts, add the new field and return.
308	if conflicts == nil {
309		tinfo.fields = append(tinfo.fields, *newf)
310		return nil
311	}
312
313	// If any conflict is shallower, ignore the new field.
314	// This matches the Go field resolution on embedding.
315	for _, i := range conflicts {
316		if len(tinfo.fields[i].idx) < len(newf.idx) {
317			return nil
318		}
319	}
320
321	// Otherwise, if any of them is at the same depth level, it's an error.
322	for _, i := range conflicts {
323		oldf := &tinfo.fields[i]
324		if len(oldf.idx) == len(newf.idx) {
325			f1 := typ.FieldByIndex(oldf.idx)
326			f2 := typ.FieldByIndex(newf.idx)
327			return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
328		}
329	}
330
331	// Otherwise, the new field is shallower, and thus takes precedence,
332	// so drop the conflicting fields from tinfo and append the new one.
333	for c := len(conflicts) - 1; c >= 0; c-- {
334		i := conflicts[c]
335		copy(tinfo.fields[i:], tinfo.fields[i+1:])
336		tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
337	}
338	tinfo.fields = append(tinfo.fields, *newf)
339	return nil
340}
341
342// A TagPathError represents an error in the unmarshalling process
343// caused by the use of field tags with conflicting paths.
344type TagPathError struct {
345	Struct       reflect.Type
346	Field1, Tag1 string
347	Field2, Tag2 string
348}
349
350func (e *TagPathError) Error() string {
351	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)
352}
353
354// value returns v's field value corresponding to finfo.
355// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
356// and dereferences pointers as necessary.
357func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
358	for i, x := range finfo.idx {
359		if i > 0 {
360			t := v.Type()
361			if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
362				if v.IsNil() {
363					v.Set(reflect.New(v.Type().Elem()))
364				}
365				v = v.Elem()
366			}
367		}
368		v = v.Field(x)
369	}
370	return v
371}
372