#!/d/Bin/Python/python.exe
# -*- coding: utf-8 -*-
#
"""


@requires:  
@license: This software is available for use under the U{W3C Software License<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>}
@organization: U{World Wide Web Consortium<http://www.w3.org>}
@author: U{Ivan Herman<a href="http://www.w3.org/People/Ivan/">}

"""

"""
$Id: GraphLiteral.py,v 1.1 2009-12-08 12:32:10 ivan Exp $ $Date: 2009-12-08 12:32:10 $
"""

__author__  = 'Ivan Herman'
__contact__ = 'Ivan Herman, ivan@w3.org'
__license__ = u'W3C® SOFTWARE NOTICE AND LICENSE, http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231'

import StringIO
from types import *

from rdflib.Graph 		import ConjunctiveGraph as Graph
from rdflib.Literal		import Literal as rdflibLiteral
from rdflib.Namespace	import Namespace
from rdflib.BNode		import BNode
from rdflibUtils		import myGraph, GraphPattern

from RDFClosure.RestrictedDatatype	import RestrictedDatatypeCore


ns_gl 						= Namespace('http://www.w3.org/2009/rdfl#')
GraphLiteral 				= ns_gl['GraphLiteral']
GraphLiteralContains		= ns_gl["GraphLiteralContains"]
GraphLiteralIsContainedBy	= ns_gl["GraphLiteralIsContainedBy"]
GL_facets 					= [ GraphLiteralContains, GraphLiteralIsContainedBy ]

class GraphLiteralType :
	def __init__(self, val) :
		def _res_or_variable(res) :
			if isinstance(res,BNode) :
				return "?%s" % str(res)
			else :
				return res
		
		# self.graph = myGraph(ConjunctiveGraph())
		self.val 	= val
		self.graph 	= myGraph()
		
		# Step 1: parse in the literal into the graph
		#  this is very crude: trying to parse first as n3, if it does not
		#  work then in xml, and if none of these work then give up
		try :
			self.graph.parse(StringIO.StringIO(val),format="n3")
		except :
			# try to parse it in xml... and raise the exception in case of a problem!
			self.graph.parse(StringIO.StringIO(val),format="xml")
		# step 2: generate a number of sparql query patterns:
		#	1. take each triple
		#	2. if there is a bnode, replace that with a ?variable using the bnode id
		#	3. add the triple to the query pattern
		self.query_pattern = GraphPattern()
		for (s,p,o) in self.graph :
			self.query_pattern.addPattern((_res_or_variable(s),_res_or_variable(p),_res_or_variable(o)))
		
	def __lt__(self, other) :
		assert( isinstance(other,GraphLiteralType) )
		# 1. take the local query pattern
		# 2. run the query as an ASK against the graph of other
		# 3. return of query is the return of this method
		retval = other.graph.queryObject(self.query_pattern).ask()
		#print "self: %s" % self
		#print "other: %s" % other
		#print retval
		#print
		return retval
		
	def __le__(self, other) :
		return self.__lt__(other)

	def __gt__(self, other) :
		return other.__lt__(self)
		
	def __ge__(self, other) :
		return self.__gt__(other)
		
	def __eq__(self, other) :
		return self.__le__(other) and other.__le__(self)
		
	def __ne__(self, other) :
		return not self.__eq__(other)
		
	def __repr__(self) :
		retval = "orig value: %s" % self.val
		#retval += "\nRDFLib graph id: %s" % self.graph
		#retval += "\nSPARQL pattern: %s" % self.query_pattern
		return retval


def _strToGraphLiteral(v) :
	"""Converting a string to a Graph Literal.
		
	@param v: the literal string defined as boolean
	@return corresponding Graph Literal value
	@rtype: GraphLiteral
	@raise ValueError: invalid rational string literal
	"""
	try :
		# An exception will be raised by the parser if the graph is incorrect;
		# no other restriction at this point
		return GraphLiteralType(v)
	except e :
		raise ValueError("Invalid Graph Literal value '%s'" % v)

		
class RestrictedGraphLiteralType(RestrictedDatatypeCore) :
	"""
	Implementation of a Graph Literal datatype with facets, ie, datatype with restrictions.	
	"""
	def __init__(self, type_uri, facets) :
		"""
		@param type_uri: URI of the datatype being defined
		@param base_type: URI of the base datatype, ie, the one being restricted
		@param facets : array of C{(facetURI, value)} pairs
		"""
		def _lit_to_value(dt, v) :
			"""
			This method is used to convert a string to a value with facet checking. RDF Literals are converted to
			Python values using this method; if there is a problem, an extension is created (and caught higher
			up to generate an error message).			
			"""
			# This may raise an exception...
			value = _strToGraphLiteral(v)
			if not dt.checkValue(value) :
				raise ValueError("Literal value %s does not fit the faceted datatype %s" % (v,dt))			
			# got here, everything should be fine
			return value
		
		RestrictedDatatypeCore.__init__(self, type_uri, GraphLiteral)

		self.contains		= []
		self.contained_by	= []
		for (facet, value) in facets :
			if   facet == GraphLiteralContains :
				self.contains.append( value )
			elif facet == GraphLiteralIsContainedBy :
				self.contained_by.append( value )
		self.toPython = lambda v : _lit_to_value(self, v)

	def checkValue(self, value) :
		"""
		Check whether the (python) value abides to the constraints defined by the current facets.
		@param value: the value to be checked
		@rtype: boolean
		"""
		try :
			for restriction in self.contains :
				if not (restriction < value) :
					return False
			for restriction in self.contained_by :
				if not (value < restriction) :
					return False
			return True
		except :
			import sys
			print sys.exc_type
			print sys.exc_value
			print sys.exc_traceback
			sys.exit(0)


