Source file src/pkg/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 }