github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/encoding/xml/typeinfo.go (about) 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 }