package rdf

import (
	
	
	
	
	
	
)

const xmlNS = "http://www.w3.org/XML/1998/namespace"

// errTooDeep aborts an RDF/XML parse whose element nesting would otherwise
// recurse deep enough to overflow the goroutine stack.
var errTooDeep = errors.New("rdf: RDF/XML nesting too deep")

// maxLiteralBytes caps the character data accumulated into a single RDF/XML
// literal. Without it, a streamed dump carrying one multi-gigabyte literal would
// buffer wholly in memory, contradicting the streaming decoder's constant-memory
// guarantee. It matches the Turtle streaming statement cap; no real literal is
// anywhere near this large.
const maxLiteralBytes = 1 << 24 // 16 MiB

// errLiteralTooLarge aborts a parse whose single literal exceeds maxLiteralBytes.
var errLiteralTooLarge = errors.New("rdf: RDF/XML literal too large")

// ParseRDFXML parses an RDF/XML document into a Graph. It supports the striped
// syntax real bibliographic RDF uses: typed node elements and rdf:Description,
// rdf:about / rdf:nodeID / rdf:ID subjects, rdf:resource and rdf:nodeID object
// references, nested node elements, literal property values with rdf:datatype and
// xml:lang, property attributes, and rdf:parseType="Resource". It does not handle
// RDF containers, reification, or rdf:parseType="Literal"/"Collection".
func ( []byte) (*Graph, error) {
	 := &Graph{}
	 := &xmlParser{dec: xml.NewDecoder(bytes.NewReader()), emit: .Add}
	if  := .run();  != nil {
		return , 
	}
	return , nil
}

// run drives the token loop, parsing each top-level node element. It is shared by
// the whole-document parser and the streaming decoder.
func ( *xmlParser) () error {
	for {
		,  := .dec.Token()
		if  == io.EOF {
			return nil
		}
		if  != nil {
			return 
		}
		,  := .(xml.StartElement)
		if ! {
			continue
		}
		// The document root is rdf:RDF (its children are node elements) or, when a
		// single node is the root, the node element itself.
		if .Name.Space == NS && .Name.Local == "RDF" {
			if  := .parseNodeChildren();  != nil {
				return 
			}
			continue
		}
		if ,  := .parseNode();  != nil {
			return 
		}
	}
}

type xmlParser struct {
	dec    *xml.Decoder
	emit   func(s, pr, o Term) // sink: Graph.Add for ParseRDFXML, channel for streaming
	blanks int
	depth  int // current node-element nesting, bounded by maxParseDepth
}

func ( *xmlParser) () Term {
	.blanks++
	return NewBlank("b" + strconv.Itoa(.blanks))
}

// docBlank wraps a document-supplied rdf:nodeID as a blank node, prefixing it so
// it can never collide with a generated ("b"+n) label -- a document nodeID of
// "b1" would otherwise merge with the first anonymous node.
func docBlank( string) Term {
	return NewBlank("u" + )
}

func iriOf( xml.Name) string { return .Space + .Local }

func attr( xml.StartElement, ,  string) (string, bool) {
	for ,  := range .Attr {
		if .Name.Space ==  && .Name.Local ==  {
			return .Value, true
		}
	}
	return "", false
}

// parseNode parses a node element (already read as a StartElement), emits its
// type and property-attribute triples, recurses into property children, and
// returns the node's subject term. It consumes the matching EndElement.
func ( *xmlParser) ( xml.StartElement) (Term, error) {
	if .depth >= maxParseDepth {
		return Term{}, errTooDeep
	}
	.depth++
	defer func() { .depth-- }()
	 := .subjectOf()
	if !(.Name.Space == NS && .Name.Local == "Description") {
		.emit(, NewIRI(TypeIRI), NewIRI(iriOf(.Name)))
	}
	// Property attributes: rdf attributes other than the identity ones become
	// literal-valued statements (rdf:type becomes a type IRI).
	for ,  := range .Attr {
		if isStructuralAttr() {
			continue
		}
		if .Name.Space == NS && .Name.Local == "type" {
			.emit(, NewIRI(TypeIRI), NewIRI(.Value))
			continue
		}
		.emit(, NewIRI(iriOf(.Name)), NewLiteral(.Value, "", ""))
	}
	if  := .parseProperties(, );  != nil {
		return , 
	}
	return , nil
}

// parseNodeChildren reads the child node elements of a container (rdf:RDF) until
// its EndElement.
func ( *xmlParser) ( xml.StartElement) error {
	for {
		,  := .dec.Token()
		if  != nil {
			return 
		}
		switch t := .(type) {
		case xml.StartElement:
			if ,  := .parseNode();  != nil {
				return 
			}
		case xml.EndElement:
			if .Name == .Name {
				return nil
			}
		}
	}
}

// parseProperties reads the property-element children of a node until its
// EndElement.
func ( *xmlParser) ( xml.StartElement,  Term) error {
	for {
		,  := .dec.Token()
		if  != nil {
			return 
		}
		switch t := .(type) {
		case xml.StartElement:
			if  := .parseProperty(, );  != nil {
				return 
			}
		case xml.EndElement:
			if .Name == .Name {
				return nil
			}
		}
	}
}

// parseProperty parses one property element and emits the (subject, predicate,
// object) triple, consuming the property's content and EndElement.
func ( *xmlParser) ( Term,  xml.StartElement) error {
	 := NewIRI(iriOf(.Name))

	if ,  := attr(, NS, "resource");  {
		.emit(, , NewIRI())
		return .skipTo(.Name)
	}
	if ,  := attr(, NS, "nodeID");  {
		.emit(, , docBlank())
		return .skipTo(.Name)
	}
	if ,  := attr(, NS, "parseType");  &&  == "Resource" {
		 := .fresh()
		.emit(, , )
		return .parseProperties(, )
	}
	,  := attr(, NS, "datatype")
	,  := attr(, xmlNS, "lang")

	// Look at the content: a nested node element, or literal text.
	var  strings.Builder
	for {
		,  := .dec.Token()
		if  != nil {
			return 
		}
		switch t := .(type) {
		case xml.StartElement:
			,  := .parseNode()
			if  != nil {
				return 
			}
			.emit(, , )
			return .skipTo(.Name) // consume any trailing whitespace + EndElement
		case xml.CharData:
			if .Len()+len() > maxLiteralBytes {
				return errLiteralTooLarge
			}
			.Write()
		case xml.EndElement:
			if .Name == .Name {
				.emit(, , NewLiteral(.String(), , ))
				return nil
			}
		}
	}
}

// skipTo consumes tokens until the EndElement matching name.
func ( *xmlParser) ( xml.Name) error {
	 := 0
	for {
		,  := .dec.Token()
		if  != nil {
			return 
		}
		switch t := .(type) {
		case xml.StartElement:
			++
		case xml.EndElement:
			if  == 0 && .Name ==  {
				return nil
			}
			--
		}
	}
}

// subjectOf returns a node element's subject from rdf:about / rdf:nodeID / rdf:ID,
// or a fresh blank node.
func ( *xmlParser) ( xml.StartElement) Term {
	if ,  := attr(, NS, "about");  {
		return NewIRI()
	}
	if ,  := attr(, NS, "nodeID");  {
		return docBlank()
	}
	if ,  := attr(, NS, "ID");  {
		return NewIRI("#" + )
	}
	return .fresh()
}

// isStructuralAttr reports whether an attribute carries node identity or XML
// machinery rather than a property statement.
func isStructuralAttr( xml.Attr) bool {
	if .Name.Space == "xmlns" || .Name.Local == "xmlns" || .Name.Space == xmlNS {
		return true
	}
	if .Name.Space == NS {
		switch .Name.Local {
		case "about", "nodeID", "ID", "resource", "datatype", "parseType":
			return true
		}
	}
	return false
}