// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package xml

import (
	
	
	
	
)

// typeInfo holds details for the xml representation of a type.
type typeInfo struct {
	xmlname *fieldInfo
	fields  []fieldInfo
}

// fieldInfo holds details for the xml representation of a single field.
type fieldInfo struct {
	idx     []int
	name    string
	xmlns   string
	flags   fieldFlags
	parents []string
}

type fieldFlags int

const (
	fElement fieldFlags = 1 << iota
	fAttr
	fCDATA
	fCharData
	fInnerXML
	fComment
	fAny

	fOmitEmpty

	fMode = fElement | fAttr | fCDATA | fCharData | fInnerXML | fComment | fAny

	xmlName = "XMLName"
)

var tinfoMap sync.Map // map[reflect.Type]*typeInfo

var nameType = reflect.TypeFor[Name]()

// getTypeInfo returns the typeInfo structure with details necessary
// for marshaling and unmarshaling typ.
func getTypeInfo( reflect.Type) (*typeInfo, error) {
	if ,  := tinfoMap.Load();  {
		return .(*typeInfo), nil
	}

	 := &typeInfo{}
	if .Kind() == reflect.Struct &&  != nameType {
		 := .NumField()
		for  := 0;  < ; ++ {
			 := .Field()
			if (!.IsExported() && !.Anonymous) || .Tag.Get("xml") == "-" {
				continue // Private field
			}

			// For embedded structs, embed its fields.
			if .Anonymous {
				 := .Type
				if .Kind() == reflect.Pointer {
					 = .Elem()
				}
				if .Kind() == reflect.Struct {
					,  := ()
					if  != nil {
						return nil, 
					}
					if .xmlname == nil {
						.xmlname = .xmlname
					}
					for ,  := range .fields {
						.idx = append([]int{}, .idx...)
						if  := addFieldInfo(, , &);  != nil {
							return nil, 
						}
					}
					continue
				}
			}

			,  := structFieldInfo(, &)
			if  != nil {
				return nil, 
			}

			if .Name == xmlName {
				.xmlname = 
				continue
			}

			// Add the field if it doesn't conflict with other fields.
			if  := addFieldInfo(, , );  != nil {
				return nil, 
			}
		}
	}

	,  := tinfoMap.LoadOrStore(, )
	return .(*typeInfo), nil
}

// structFieldInfo builds and returns a fieldInfo for f.
func structFieldInfo( reflect.Type,  *reflect.StructField) (*fieldInfo, error) {
	 := &fieldInfo{idx: .Index}

	// Split the tag from the xml namespace if necessary.
	 := .Tag.Get("xml")
	if , ,  := strings.Cut(, " ");  {
		.xmlns,  = , 
	}

	// Parse flags.
	 := strings.Split(, ",")
	if len() == 1 {
		.flags = fElement
	} else {
		 = [0]
		for ,  := range [1:] {
			switch  {
			case "attr":
				.flags |= fAttr
			case "cdata":
				.flags |= fCDATA
			case "chardata":
				.flags |= fCharData
			case "innerxml":
				.flags |= fInnerXML
			case "comment":
				.flags |= fComment
			case "any":
				.flags |= fAny
			case "omitempty":
				.flags |= fOmitEmpty
			}
		}

		// Validate the flags used.
		 := true
		switch  := .flags & fMode;  {
		case 0:
			.flags |= fElement
		case fAttr, fCDATA, fCharData, fInnerXML, fComment, fAny, fAny | fAttr:
			if .Name == xmlName ||  != "" &&  != fAttr {
				 = false
			}
		default:
			// This will also catch multiple modes in a single field.
			 = false
		}
		if .flags&fMode == fAny {
			.flags |= fElement
		}
		if .flags&fOmitEmpty != 0 && .flags&(fElement|fAttr) == 0 {
			 = false
		}
		if ! {
			return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
				.Name, , .Tag.Get("xml"))
		}
	}

	// Use of xmlns without a name is not allowed.
	if .xmlns != "" &&  == "" {
		return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
			.Name, , .Tag.Get("xml"))
	}

	if .Name == xmlName {
		// The XMLName field records the XML element name. Don't
		// process it as usual because its name should default to
		// empty rather than to the field name.
		.name = 
		return , nil
	}

	if  == "" {
		// If the name part of the tag is completely empty, get
		// default from XMLName of underlying struct if feasible,
		// or field name otherwise.
		if  := lookupXMLName(.Type);  != nil {
			.xmlns, .name = .xmlns, .name
		} else {
			.name = .Name
		}
		return , nil
	}

	// Prepare field name and parents.
	 := strings.Split(, ">")
	if [0] == "" {
		[0] = .Name
	}
	if [len()-1] == "" {
		return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", .Name, )
	}
	.name = [len()-1]
	if len() > 1 {
		if (.flags & fElement) == 0 {
			return nil, fmt.Errorf("xml: %s chain not valid with %s flag", , strings.Join([1:], ","))
		}
		.parents = [:len()-1]
	}

	// If the field type has an XMLName field, the names must match
	// so that the behavior of both marshaling and unmarshaling
	// is straightforward and unambiguous.
	if .flags&fElement != 0 {
		 := .Type
		 := lookupXMLName()
		if  != nil && .name != .name {
			return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
				.name, , .Name, .name, )
		}
	}
	return , nil
}

// lookupXMLName returns the fieldInfo for typ's XMLName field
// in case it exists and has a valid xml field tag, otherwise
// it returns nil.
func lookupXMLName( reflect.Type) ( *fieldInfo) {
	for .Kind() == reflect.Pointer {
		 = .Elem()
	}
	if .Kind() != reflect.Struct {
		return nil
	}
	for ,  := 0, .NumField();  < ; ++ {
		 := .Field()
		if .Name != xmlName {
			continue
		}
		,  := structFieldInfo(, &)
		if  == nil && .name != "" {
			return 
		}
		// Also consider errors as a non-existent field tag
		// and let getTypeInfo itself report the error.
		break
	}
	return nil
}

// addFieldInfo adds finfo to tinfo.fields if there are no
// conflicts, or if conflicts arise from previous fields that were
// obtained from deeper embedded structures than finfo. In the latter
// case, the conflicting entries are dropped.
// A conflict occurs when the path (parent + name) to a field is
// itself a prefix of another path, or when two paths match exactly.
// It is okay for field paths to share a common, shorter prefix.
func addFieldInfo( reflect.Type,  *typeInfo,  *fieldInfo) error {
	var  []int
:
	// First, figure all conflicts. Most working code will have none.
	for  := range .fields {
		 := &.fields[]
		if .flags&fMode != .flags&fMode {
			continue
		}
		if .xmlns != "" && .xmlns != "" && .xmlns != .xmlns {
			continue
		}
		 := min(len(.parents), len(.parents))
		for  := 0;  < ; ++ {
			if .parents[] != .parents[] {
				continue 
			}
		}
		if len(.parents) > len(.parents) {
			if .parents[len(.parents)] == .name {
				 = append(, )
			}
		} else if len(.parents) < len(.parents) {
			if .parents[len(.parents)] == .name {
				 = append(, )
			}
		} else {
			if .name == .name && .xmlns == .xmlns {
				 = append(, )
			}
		}
	}
	// Without conflicts, add the new field and return.
	if  == nil {
		.fields = append(.fields, *)
		return nil
	}

	// If any conflict is shallower, ignore the new field.
	// This matches the Go field resolution on embedding.
	for ,  := range  {
		if len(.fields[].idx) < len(.idx) {
			return nil
		}
	}

	// Otherwise, if any of them is at the same depth level, it's an error.
	for ,  := range  {
		 := &.fields[]
		if len(.idx) == len(.idx) {
			 := .FieldByIndex(.idx)
			 := .FieldByIndex(.idx)
			return &TagPathError{, .Name, .Tag.Get("xml"), .Name, .Tag.Get("xml")}
		}
	}

	// Otherwise, the new field is shallower, and thus takes precedence,
	// so drop the conflicting fields from tinfo and append the new one.
	for  := len() - 1;  >= 0; -- {
		 := []
		copy(.fields[:], .fields[+1:])
		.fields = .fields[:len(.fields)-1]
	}
	.fields = append(.fields, *)
	return nil
}

// A TagPathError represents an error in the unmarshaling process
// caused by the use of field tags with conflicting paths.
type TagPathError struct {
	Struct       reflect.Type
	Field1, Tag1 string
	Field2, Tag2 string
}

func ( *TagPathError) () string {
	return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", .Struct, .Field1, .Tag1, .Field2, .Tag2)
}

const (
	initNilPointers     = true
	dontInitNilPointers = false
)

// value returns v's field value corresponding to finfo.
// It's equivalent to v.FieldByIndex(finfo.idx), but when passed
// initNilPointers, it initializes and dereferences pointers as necessary.
// When passed dontInitNilPointers and a nil pointer is reached, the function
// returns a zero reflect.Value.
func ( *fieldInfo) ( reflect.Value,  bool) reflect.Value {
	for ,  := range .idx {
		if  > 0 {
			 := .Type()
			if .Kind() == reflect.Pointer && .Elem().Kind() == reflect.Struct {
				if .IsNil() {
					if ! {
						return reflect.Value{}
					}
					.Set(reflect.New(.Type().Elem()))
				}
				 = .Elem()
			}
		}
		 = .Field()
	}
	return 
}