OSDN Git Service

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