OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / webdav / internal / 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                                         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.
114 func 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.
238 func 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
261 func 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.
275 func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
276         var conflicts []int
277 Loop:
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.
344 type TagPathError struct {
345         Struct       reflect.Type
346         Field1, Tag1 string
347         Field2, Tag2 string
348 }
349
350 func (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.
357 func (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 }