"""
Skeleton of an RDFa file handling. Tries to write down the status of the discussions for the
RDFa spec of the beginning of July of 2007.

The functions beginning with '_' are 'helper' functions, the details of those are not of interest
at first reading!

The correct namespace handling is not done. In general, when reading attributes, the local name and the prefix
should be separated and the prefix could be used to access RDFLib Namespace objects. To be done.

$id:$, $Date: 2007/08/02 06:47:35 $


"""

#######################################################################
# Function to check whether one of a series of attributes
# is part of the DOM Node
def _hasOneOfAttributes(node,*args) :
	"""
	Check whether one of the listed attributes is present on a node.
	@param node: DOM element node
	@param args: tuple consisting of possible attributes
	"""
	return True in [ node.hasAttribute(attr) for attr in args ]



# Turn the phony list elements into real lists
def _finalizeLists(graph) :
	"""The generation process adds phony properties into the graph ("phonyElement_X") to simulate
	lists; these have to be taken out and turned into proper RDF lists...
	@param graph: the full graph
	"""
	for lst in graph.triples((None,ns_rdf["type"],ns_rdf["List"])) :
		# get the phony properties...
		# we have to extract them into a list because we will have to order it...
		els = [ (prop,res) for (prop,res) in graph.triples((lst,None,Nonde)) ]
		els.sort(key=lambda el: el[0]) #....
		currList = lst
		for i in range(0,len(els)) :
			(prop,res) = els[i]
			graph.remove((lst,prop,res))
			graph.add((currList,ns_rdf["first"],res))
			if i == len(els) - 1 :
				# end of the list...
				graph.add((currList,ns_rdf["next"],ns_rdf["nil"]))
			else :
				# Create a new list:
				newList = BNode()
				graph.add((newList,ns_rdf["type"],ns_rdf["List"]))
				graph.add((currList,ns_rdf["next"],newList))
				currList = newList
	# that is folks...

####################################################################################
# Implementation of the literal generation for a property

def generateLiteral(node,graph) :
	"""Generate the literal for the specific property, taking into account datatype, etc.
	Note: this method is called only if the @property is indeed present, no need to check and
	the @content property is also forgotten, because treated elsewhere...

	This should be the encoding of the algorithm well documented already on the wiki page

	@param node: DOM element node
	@param graph: the (RDF) graph to add the properies to
	"""
	assert node.hasAttribute("property")
	# to be done...

####################################################################################

def manageOneNode(node,graph,currentSubject,index) :
	"""The (recursive) step of handling a single node when running down
	the DOM tree.

	Note: the current pseudo-code does not handle the namespaces yet. It just takes the
	various attribute values verbatim, which is wrong. To be done...

	@param node: the DOM node to handle
	@param graph: the RDF graph
	@param graph: the current subject, as an RDFLib URIRef
	@param index: number of the element among siblings. This is necessary for possible
	lists or collections
	"""
	# -------------------------------------------------------------------------------
	# Take care of the <li> element cases. This might inject new attributes to the current
	# node!
	# @@@@A more proper way of doing this should include namespace handling@@@@
	lstOrColl = None
	if node.tagName == "li" and ( node.parentNode.tagName == "ul" or node.parent.tagName == "ol" ) :
		# Well, we still have to see whether the parent used an RDFa construct
		# That can be checked by looking at the currentSubject: the graph should already
		# include typing for rdf:List, rdf:Alt, etc....
		if (currentSubject,ns_rdf["type"],ns_rdf["List"]) in graph :
			# yep, it is a list!
			lstOrColl = "phonyElement_%s" % index
		elif (currentSubject,ns_rdf["type"],ns_rdf["Seq"]) in graph or (currentSubject,ns_rdf["type"],ns_rdf["Alt"]) in graph or (currentSubject,ns_rdf["type"],ns_rdf["Bag"]) in graph :
			# The proper prefix for the property should be found!!!
			lstOrColl = "rdf:_%s" % index
		# Yes, we have to add an extra attribute!
		if _hasOneOfAttributes("href","resource","instanceof") :
			node.setAttribute("rel",lstOrColl)
		else :
			node.setAttribute("property",lstOrColl)

	# First, let us check whether there is anything to do at all. Ie,
	# whether there is any RDFa specific attribute on the element
	if not _hasOneOfAttributes(node,"href","resource","about","property","instanceof","rel","rev") :
		# nop, there is nothing to do here, just go down the tree...
		for n in node.childNodes :
			if n.nodeType == Node.ELEMENT_NODE :
				manageOneNode(n,graph,currentSubject,0) # The 'index' attribute is meaningless for this case...
		return
	#
	# there *is* something to do:-)
	# -------------------------------------------------------------------------------
	# 1. decide whether the currentSubject should stay valid, or whether it is
	# overwritten by a local @about parameter.
	# the "subject" variable will be the final current subject
	# Note that this may have side-effects for an <li> element, ie, if somebody manually sets
	# the @about in a list, it may override the collection/list generation. Well, that is it...
	# (Alternatively, we may decide to disallow this, ie, to run the code below only
	# if lstOrColl != None
	if node.hasAttribute("about") :
		v = node.getAttribute("about")
		# @@@@ this is wrong, a correct namespace handling should come here!!! @@@@@
		subject = URIRef(v)
	else :
		subject = currentSubject

	# --------------------------------------------------------------------------------
	# 2. See if a new subject will have to be generated for the containing elements.
	# The algorithm is, essentially, the one described by Ben in
	# http://www.w3.org/mid/46A8D3ED.2080404@adida.net
	# except that the @about is taken out
	# I have also added @data for <object>, can be taken out easily...
	if _hasOneOfAttributes(node,"rel","rev","instanceof") :
		# These are the cases when a new subject has to be created!
		val        = None
		if node.hasAttribute("resource") :
			val = node.getAttribute("resource")
		elif node.hasAttribute("href") :
			val = node.getAttribute("href")
		elif node.hasAttribute("src") :
			val = node.getAttribute("src")
		elif node.hasAttribute("data") and node.tagName == "object" :
			val = node.getAttribute("data")
		# see if there is a specific node here
		if val :
			newSubject = URIRef(val) # to be done with proper namespaces...
		else :
			newSubject = BNode()
		# handling the @instanceof
		tp = URIRef(node.getAttribute("instanceof"))   # Namespace handling again...
		graph.add((newSubject,ns_rdf["type"],tp))

		# Handle the @rel and @rev triples
		if node.hasAttribute("rel") :
			prop = URIRef(node.getAttribute("rel"))
			graph.add((subject,prop,newSubject))
		if node.hasAttribute("rev") :
			prop = URIRef(node.getAttribute("rev"))
			graph.add((newSubject,prop,subject))

		# Here comes the essence of Ben's move: the subject for descendents will be the newly generated one..
		subject = newSubject

	# --------------------------------------------------------------------------------
	# 3. Handle the possible @property attribute. If there is property
	# and no @content, then the child elements do not have to be visited
	if node.hasAttribute("property") :
		if node.hasAttribute("content") :
			# this is the simple case: add a new triple and go in with the rest
			# of the processing
			prop = node.getAttribute("property")
			val  = node.getAttribute("content")
			# @@@ proper namespace handling should be put here!!! @@@
			graph.add((currentSubject,URIRef(prop),Literal(val)))
		else :
			# this is put into a separate function to make it cleaner in the code
			generateLiteral(node,graph)
			return #!!!!!

	# -------------------------------------------------------------------------------
	# Everything is settled for the current node. Go through the children...
	index = 1
	for n in node.childNodes :
		if n.nodeType == Node.ELEMENT_NODE :
			manageOneNode(n,graph,subject,index)
			index += 1

	# -------------------------------------------------------------------
	# This should be it...
	# -------------------------------------------------------------------

##################################################################################

def main() :
	# The main processing steps
	graph   = Graph()
	subject = URIRef("")      # The URI Ref of the file itself should be added here, of @xml:base
	top     = getTopDOMNode() # Lots of boring details here on parsing the XML tree, and get the
	                          # element node for <h>. The namespace management should also be initialized here!
	manageOneNode(top,graph,subject,0)
	_finalizeLists(graph)     # generate a proper set of lists...
	graph.serialize(format="pretty-xml")


###################################################################################
#
# $Log: rdfa.py,v $
# Revision 1.3  2007/08/02 06:47:35  ivan
# The order of processing properties vs the others was wrong, a situation
# where
#
# <span property=".." rel="..." resource="..."/>
#
# was not handled properly (the rel part was skipped). The property management
# is now done at the end, before possibly going through the children
#
#
#



