Source file src/pkg/encoding/xml/typeinfo.go
1
2
3
4
5 package xml
6
7 import (
8 "fmt"
9 "reflect"
10 "strings"
11 "sync"
12 )
13
14
15 type typeInfo struct {
16 xmlname *fieldInfo
17 fields []fieldInfo
18 }
19
20
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
50
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
65 }
66
67
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
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
111 func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
112 finfo := &fieldInfo{idx: f.Index}
113
114
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
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
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
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
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
176
177
178 finfo.name = tag
179 return finfo, nil
180 }
181
182 if tag == "" {
183
184
185
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
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
211
212
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
225
226
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
244
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
258
259
260
261
262
263
264 func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
265 var conflicts []int
266 Loop:
267
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
297 if conflicts == nil {
298 tinfo.fields = append(tinfo.fields, *newf)
299 return nil
300 }
301
302
303
304 for _, i := range conflicts {
305 if len(tinfo.fields[i].idx) < len(newf.idx) {
306 return nil
307 }
308 }
309
310
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
321
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
332
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
344
345
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 }
View as plain text