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