libgo: update to go1.9
[official-gcc.git] / libgo / go / encoding / xml / typeinfo.go
blob751caa97aa1b5e7bedaeb9c6d2798ef4c395b2e1
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.
5 package xml
7 import (
8 "fmt"
9 "reflect"
10 "strings"
11 "sync"
14 // typeInfo holds details for the xml representation of a type.
15 type typeInfo struct {
16 xmlname *fieldInfo
17 fields []fieldInfo
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
29 type fieldFlags int
31 const (
32 fElement fieldFlags = 1 << iota
33 fAttr
34 fCDATA
35 fCharData
36 fInnerXml
37 fComment
38 fAny
40 fOmitEmpty
42 fMode = fElement | fAttr | fCDATA | fCharData | fInnerXml | fComment | fAny
45 var tinfoMap sync.Map // map[reflect.Type]*typeInfo
47 var nameType = reflect.TypeOf(Name{})
49 // getTypeInfo returns the typeInfo structure with details necessary
50 // for marshaling and unmarshaling typ.
51 func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
52 if ti, ok := tinfoMap.Load(typ); ok {
53 return ti.(*typeInfo), nil
56 tinfo := &typeInfo{}
57 if typ.Kind() == reflect.Struct && typ != nameType {
58 n := typ.NumField()
59 for i := 0; i < n; i++ {
60 f := typ.Field(i)
61 if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get("xml") == "-" {
62 continue // Private field
65 // For embedded structs, embed its fields.
66 if f.Anonymous {
67 t := f.Type
68 if t.Kind() == reflect.Ptr {
69 t = t.Elem()
71 if t.Kind() == reflect.Struct {
72 inner, err := getTypeInfo(t)
73 if err != nil {
74 return nil, err
76 if tinfo.xmlname == nil {
77 tinfo.xmlname = inner.xmlname
79 for _, finfo := range inner.fields {
80 finfo.idx = append([]int{i}, finfo.idx...)
81 if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
82 return nil, err
85 continue
89 finfo, err := structFieldInfo(typ, &f)
90 if err != nil {
91 return nil, err
94 if f.Name == "XMLName" {
95 tinfo.xmlname = finfo
96 continue
99 // Add the field if it doesn't conflict with other fields.
100 if err := addFieldInfo(typ, tinfo, finfo); err != nil {
101 return nil, err
106 ti, _ := tinfoMap.LoadOrStore(typ, tinfo)
107 return ti.(*typeInfo), nil
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}
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:]
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 "cdata":
131 finfo.flags |= fCDATA
132 case "chardata":
133 finfo.flags |= fCharData
134 case "innerxml":
135 finfo.flags |= fInnerXml
136 case "comment":
137 finfo.flags |= fComment
138 case "any":
139 finfo.flags |= fAny
140 case "omitempty":
141 finfo.flags |= fOmitEmpty
145 // Validate the flags used.
146 valid := true
147 switch mode := finfo.flags & fMode; mode {
148 case 0:
149 finfo.flags |= fElement
150 case fAttr, fCDATA, fCharData, fInnerXml, fComment, fAny, fAny | fAttr:
151 if f.Name == "XMLName" || tag != "" && mode != fAttr {
152 valid = false
154 default:
155 // This will also catch multiple modes in a single field.
156 valid = false
158 if finfo.flags&fMode == fAny {
159 finfo.flags |= fElement
161 if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
162 valid = false
164 if !valid {
165 return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
166 f.Name, typ, f.Tag.Get("xml"))
170 // Use of xmlns without a name is not allowed.
171 if finfo.xmlns != "" && tag == "" {
172 return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
173 f.Name, typ, f.Tag.Get("xml"))
176 if f.Name == "XMLName" {
177 // The XMLName field records the XML element name. Don't
178 // process it as usual because its name should default to
179 // empty rather than to the field name.
180 finfo.name = tag
181 return finfo, nil
184 if tag == "" {
185 // If the name part of the tag is completely empty, get
186 // default from XMLName of underlying struct if feasible,
187 // or field name otherwise.
188 if xmlname := lookupXMLName(f.Type); xmlname != nil {
189 finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
190 } else {
191 finfo.name = f.Name
193 return finfo, nil
196 // Prepare field name and parents.
197 parents := strings.Split(tag, ">")
198 if parents[0] == "" {
199 parents[0] = f.Name
201 if parents[len(parents)-1] == "" {
202 return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
204 finfo.name = parents[len(parents)-1]
205 if len(parents) > 1 {
206 if (finfo.flags & fElement) == 0 {
207 return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
209 finfo.parents = parents[:len(parents)-1]
212 // If the field type has an XMLName field, the names must match
213 // so that the behavior of both marshaling and unmarshaling
214 // is straightforward and unambiguous.
215 if finfo.flags&fElement != 0 {
216 ftyp := f.Type
217 xmlname := lookupXMLName(ftyp)
218 if xmlname != nil && xmlname.name != finfo.name {
219 return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
220 finfo.name, typ, f.Name, xmlname.name, ftyp)
223 return finfo, nil
226 // lookupXMLName returns the fieldInfo for typ's XMLName field
227 // in case it exists and has a valid xml field tag, otherwise
228 // it returns nil.
229 func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
230 for typ.Kind() == reflect.Ptr {
231 typ = typ.Elem()
233 if typ.Kind() != reflect.Struct {
234 return nil
236 for i, n := 0, typ.NumField(); i < n; i++ {
237 f := typ.Field(i)
238 if f.Name != "XMLName" {
239 continue
241 finfo, err := structFieldInfo(typ, &f)
242 if finfo.name != "" && err == nil {
243 return finfo
245 // Also consider errors as a non-existent field tag
246 // and let getTypeInfo itself report the error.
247 break
249 return nil
252 func min(a, b int) int {
253 if a <= b {
254 return a
256 return b
259 // addFieldInfo adds finfo to tinfo.fields if there are no
260 // conflicts, or if conflicts arise from previous fields that were
261 // obtained from deeper embedded structures than finfo. In the latter
262 // case, the conflicting entries are dropped.
263 // A conflict occurs when the path (parent + name) to a field is
264 // itself a prefix of another path, or when two paths match exactly.
265 // It is okay for field paths to share a common, shorter prefix.
266 func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
267 var conflicts []int
268 Loop:
269 // First, figure all conflicts. Most working code will have none.
270 for i := range tinfo.fields {
271 oldf := &tinfo.fields[i]
272 if oldf.flags&fMode != newf.flags&fMode {
273 continue
275 if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
276 continue
278 minl := min(len(newf.parents), len(oldf.parents))
279 for p := 0; p < minl; p++ {
280 if oldf.parents[p] != newf.parents[p] {
281 continue Loop
284 if len(oldf.parents) > len(newf.parents) {
285 if oldf.parents[len(newf.parents)] == newf.name {
286 conflicts = append(conflicts, i)
288 } else if len(oldf.parents) < len(newf.parents) {
289 if newf.parents[len(oldf.parents)] == oldf.name {
290 conflicts = append(conflicts, i)
292 } else {
293 if newf.name == oldf.name {
294 conflicts = append(conflicts, i)
298 // Without conflicts, add the new field and return.
299 if conflicts == nil {
300 tinfo.fields = append(tinfo.fields, *newf)
301 return nil
304 // If any conflict is shallower, ignore the new field.
305 // This matches the Go field resolution on embedding.
306 for _, i := range conflicts {
307 if len(tinfo.fields[i].idx) < len(newf.idx) {
308 return nil
312 // Otherwise, if any of them is at the same depth level, it's an error.
313 for _, i := range conflicts {
314 oldf := &tinfo.fields[i]
315 if len(oldf.idx) == len(newf.idx) {
316 f1 := typ.FieldByIndex(oldf.idx)
317 f2 := typ.FieldByIndex(newf.idx)
318 return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
322 // Otherwise, the new field is shallower, and thus takes precedence,
323 // so drop the conflicting fields from tinfo and append the new one.
324 for c := len(conflicts) - 1; c >= 0; c-- {
325 i := conflicts[c]
326 copy(tinfo.fields[i:], tinfo.fields[i+1:])
327 tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
329 tinfo.fields = append(tinfo.fields, *newf)
330 return nil
333 // A TagPathError represents an error in the unmarshaling process
334 // caused by the use of field tags with conflicting paths.
335 type TagPathError struct {
336 Struct reflect.Type
337 Field1, Tag1 string
338 Field2, Tag2 string
341 func (e *TagPathError) Error() string {
342 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 // value returns v's field value corresponding to finfo.
346 // It's equivalent to v.FieldByIndex(finfo.idx), but initializes
347 // and dereferences pointers as necessary.
348 func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
349 for i, x := range finfo.idx {
350 if i > 0 {
351 t := v.Type()
352 if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
353 if v.IsNil() {
354 v.Set(reflect.New(v.Type().Elem()))
356 v = v.Elem()
359 v = v.Field(x)
361 return v