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