OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [encoding/] [xml/] [typeinfo.go] - Blame information for rev 747

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 747 jeremybenn
// 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
        // TODO:
40
        //fOmitEmpty
41
 
42
        fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
43
)
44
 
45
var tinfoMap = make(map[reflect.Type]*typeInfo)
46
var tinfoLock sync.RWMutex
47
 
48
var nameType = reflect.TypeOf(Name{})
49
 
50
// getTypeInfo returns the typeInfo structure with details necessary
51
// for marshalling and unmarshalling typ.
52
func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
53
        tinfoLock.RLock()
54
        tinfo, ok := tinfoMap[typ]
55
        tinfoLock.RUnlock()
56
        if ok {
57
                return tinfo, nil
58
        }
59
        tinfo = &typeInfo{}
60
        if typ.Kind() == reflect.Struct && typ != nameType {
61
                n := typ.NumField()
62
                for i := 0; i < n; i++ {
63
                        f := typ.Field(i)
64
                        if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
65
                                continue // Private field
66
                        }
67
 
68
                        // For embedded structs, embed its fields.
69
                        if f.Anonymous {
70
                                if f.Type.Kind() != reflect.Struct {
71
                                        continue
72
                                }
73
                                inner, err := getTypeInfo(f.Type)
74
                                if err != nil {
75
                                        return nil, err
76
                                }
77
                                for _, finfo := range inner.fields {
78
                                        finfo.idx = append([]int{i}, finfo.idx...)
79
                                        if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
80
                                                return nil, err
81
                                        }
82
                                }
83
                                continue
84
                        }
85
 
86
                        finfo, err := structFieldInfo(typ, &f)
87
                        if err != nil {
88
                                return nil, err
89
                        }
90
 
91
                        if f.Name == "XMLName" {
92
                                tinfo.xmlname = finfo
93
                                continue
94
                        }
95
 
96
                        // Add the field if it doesn't conflict with other fields.
97
                        if err := addFieldInfo(typ, tinfo, finfo); err != nil {
98
                                return nil, err
99
                        }
100
                }
101
        }
102
        tinfoLock.Lock()
103
        tinfoMap[typ] = tinfo
104
        tinfoLock.Unlock()
105
        return tinfo, nil
106
}
107
 
108
// structFieldInfo builds and returns a fieldInfo for f.
109
func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
110
        finfo := &fieldInfo{idx: f.Index}
111
 
112
        // Split the tag from the xml namespace if necessary.
113
        tag := f.Tag.Get("xml")
114
        if i := strings.Index(tag, " "); i >= 0 {
115
                finfo.xmlns, tag = tag[:i], tag[i+1:]
116
        }
117
 
118
        // Parse flags.
119
        tokens := strings.Split(tag, ",")
120
        if len(tokens) == 1 {
121
                finfo.flags = fElement
122
        } else {
123
                tag = tokens[0]
124
                for _, flag := range tokens[1:] {
125
                        switch flag {
126
                        case "attr":
127
                                finfo.flags |= fAttr
128
                        case "chardata":
129
                                finfo.flags |= fCharData
130
                        case "innerxml":
131
                                finfo.flags |= fInnerXml
132
                        case "comment":
133
                                finfo.flags |= fComment
134
                        case "any":
135
                                finfo.flags |= fAny
136
                        }
137
                }
138
 
139
                // Validate the flags used.
140
                switch mode := finfo.flags & fMode; mode {
141
                case 0:
142
                        finfo.flags |= fElement
143
                case fAttr, fCharData, fInnerXml, fComment, fAny:
144
                        if f.Name != "XMLName" && (tag == "" || mode == fAttr) {
145
                                break
146
                        }
147
                        fallthrough
148
                default:
149
                        // This will also catch multiple modes in a single field.
150
                        return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
151
                                f.Name, typ, f.Tag.Get("xml"))
152
                }
153
        }
154
 
155
        // Use of xmlns without a name is not allowed.
156
        if finfo.xmlns != "" && tag == "" {
157
                return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
158
                        f.Name, typ, f.Tag.Get("xml"))
159
        }
160
 
161
        if f.Name == "XMLName" {
162
                // The XMLName field records the XML element name. Don't
163
                // process it as usual because its name should default to
164
                // empty rather than to the field name.
165
                finfo.name = tag
166
                return finfo, nil
167
        }
168
 
169
        if tag == "" {
170
                // If the name part of the tag is completely empty, get
171
                // default from XMLName of underlying struct if feasible,
172
                // or field name otherwise.
173
                if xmlname := lookupXMLName(f.Type); xmlname != nil {
174
                        finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
175
                } else {
176
                        finfo.name = f.Name
177
                }
178
                return finfo, nil
179
        }
180
 
181
        // Prepare field name and parents.
182
        tokens = strings.Split(tag, ">")
183
        if tokens[0] == "" {
184
                tokens[0] = f.Name
185
        }
186
        if tokens[len(tokens)-1] == "" {
187
                return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
188
        }
189
        finfo.name = tokens[len(tokens)-1]
190
        if len(tokens) > 1 {
191
                finfo.parents = tokens[:len(tokens)-1]
192
        }
193
 
194
        // If the field type has an XMLName field, the names must match
195
        // so that the behavior of both marshalling and unmarshalling
196
        // is straightforward and unambiguous.
197
        if finfo.flags&fElement != 0 {
198
                ftyp := f.Type
199
                xmlname := lookupXMLName(ftyp)
200
                if xmlname != nil && xmlname.name != finfo.name {
201
                        return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
202
                                finfo.name, typ, f.Name, xmlname.name, ftyp)
203
                }
204
        }
205
        return finfo, nil
206
}
207
 
208
// lookupXMLName returns the fieldInfo for typ's XMLName field
209
// in case it exists and has a valid xml field tag, otherwise
210
// it returns nil.
211
func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
212
        for typ.Kind() == reflect.Ptr {
213
                typ = typ.Elem()
214
        }
215
        if typ.Kind() != reflect.Struct {
216
                return nil
217
        }
218
        for i, n := 0, typ.NumField(); i < n; i++ {
219
                f := typ.Field(i)
220
                if f.Name != "XMLName" {
221
                        continue
222
                }
223
                finfo, err := structFieldInfo(typ, &f)
224
                if finfo.name != "" && err == nil {
225
                        return finfo
226
                }
227
                // Also consider errors as a non-existent field tag
228
                // and let getTypeInfo itself report the error.
229
                break
230
        }
231
        return nil
232
}
233
 
234
func min(a, b int) int {
235
        if a <= b {
236
                return a
237
        }
238
        return b
239
}
240
 
241
// addFieldInfo adds finfo to tinfo.fields if there are no
242
// conflicts, or if conflicts arise from previous fields that were
243
// obtained from deeper embedded structures than finfo. In the latter
244
// case, the conflicting entries are dropped.
245
// A conflict occurs when the path (parent + name) to a field is
246
// itself a prefix of another path, or when two paths match exactly.
247
// It is okay for field paths to share a common, shorter prefix.
248
func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
249
        var conflicts []int
250
Loop:
251
        // First, figure all conflicts. Most working code will have none.
252
        for i := range tinfo.fields {
253
                oldf := &tinfo.fields[i]
254
                if oldf.flags&fMode != newf.flags&fMode {
255
                        continue
256
                }
257
                minl := min(len(newf.parents), len(oldf.parents))
258
                for p := 0; p < minl; p++ {
259
                        if oldf.parents[p] != newf.parents[p] {
260
                                continue Loop
261
                        }
262
                }
263
                if len(oldf.parents) > len(newf.parents) {
264
                        if oldf.parents[len(newf.parents)] == newf.name {
265
                                conflicts = append(conflicts, i)
266
                        }
267
                } else if len(oldf.parents) < len(newf.parents) {
268
                        if newf.parents[len(oldf.parents)] == oldf.name {
269
                                conflicts = append(conflicts, i)
270
                        }
271
                } else {
272
                        if newf.name == oldf.name {
273
                                conflicts = append(conflicts, i)
274
                        }
275
                }
276
        }
277
        // Without conflicts, add the new field and return.
278
        if conflicts == nil {
279
                tinfo.fields = append(tinfo.fields, *newf)
280
                return nil
281
        }
282
 
283
        // If any conflict is shallower, ignore the new field.
284
        // This matches the Go field resolution on embedding.
285
        for _, i := range conflicts {
286
                if len(tinfo.fields[i].idx) < len(newf.idx) {
287
                        return nil
288
                }
289
        }
290
 
291
        // Otherwise, if any of them is at the same depth level, it's an error.
292
        for _, i := range conflicts {
293
                oldf := &tinfo.fields[i]
294
                if len(oldf.idx) == len(newf.idx) {
295
                        f1 := typ.FieldByIndex(oldf.idx)
296
                        f2 := typ.FieldByIndex(newf.idx)
297
                        return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
298
                }
299
        }
300
 
301
        // Otherwise, the new field is shallower, and thus takes precedence,
302
        // so drop the conflicting fields from tinfo and append the new one.
303
        for c := len(conflicts) - 1; c >= 0; c-- {
304
                i := conflicts[c]
305
                copy(tinfo.fields[i:], tinfo.fields[i+1:])
306
                tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
307
        }
308
        tinfo.fields = append(tinfo.fields, *newf)
309
        return nil
310
}
311
 
312
// A TagPathError represents an error in the unmarshalling process
313
// caused by the use of field tags with conflicting paths.
314
type TagPathError struct {
315
        Struct       reflect.Type
316
        Field1, Tag1 string
317
        Field2, Tag2 string
318
}
319
 
320
func (e *TagPathError) Error() string {
321
        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)
322
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.