#!/usr/bin/python

import re, string, time
import rng
import rngCoreTypeLib

"""
The contents of this file are subject to the Mozilla Public License  
Version 1.1 (the "License"); you may not use this file except in  
compliance with the License. 
You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
Software distributed under the License is distributed on an "AS IS"  
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the  
License for the specific language governing rights and limitations under  
the License. 

The Original Code is available at http://downloads.xmlschemata.org/python/xvif/

The Initial Developer of the Original Code is Eric van der Vlist. Portions  
created by Eric van der Vlist are Copyright (C) 2002. All Rights Reserved. 

Relax NG is a specification edited by the OASIS RELAX NG Technical Committee:
http://www.oasis-open.org/committees/relax-ng/

This implementation uses the implementation notes written by James Clark:
http://www.thaiopensource.com/relaxng/implement.html

Contributor(s): 

"""

"""

A type library is a module with a set of classes with a "Type" suffix.

The association with the datatype URI is done through the "typeLibraries"
variable of the rng.RngParser class.

The library modules are loaded dynamically and the association between a
type and the class implementing it is done by introspection.


"""

class _Lexical:

	def __getattr__(self, name):
		if name=="lexical":
			return self
		else:
			raise AttributeError, name

	if len(u'\U00010800') == 1:
		def lengthFacet(self, value):
			return len(self.lexical) == int(value)
	else:
		def lengthFacet(self, value):
			import Smart_len
			return Smart_len.smart_len(self) == int(value)


class stringType(rngCoreTypeLib.stringType, _Lexical):
	""" """

class normalizedStringType(stringType):

	def __new__(cls, value=""):
		return unicode.__new__(cls, re.sub("[\n\t]", " ", value))

class tokenType(rngCoreTypeLib.tokenType, _Lexical):
 	"""
		This class is strictly identical to the token builtin class
 	"""

class _Bounded:

	def __init__(self, value):
		if self.__class__.max != None and self > self.__class__.max:
			raise ValueError
		if self.__class__.min != None and self < self.__class__.min:
			raise ValueError
	

class _Ordered:

	def maxExclusiveFacet(self, value):
		return self < self.__class__(value)

	def maxInclusiveFacet(self, value):
		return self <= self.__class__(value)

	def minExclusiveFacet(self, value):
		return self > self.__class__(value)

	def minInclusiveFacet(self, value):
		return self >= self.__class__(value)

class _Numeric(_Ordered):

	def totalDigitsFacet(self, value):
		if self < 0:
			return len(str(self)) -1 <= value
		else:
			return len(str(self))  <= value
			

class integerType(long, _Numeric):
	"""
	"""

class nonPositiveIntegerType(_Bounded, integerType):

	min=None
	max=0

class nonNegativeIntegerType(_Bounded, integerType):

	min=0
	max=None

class positiveIntegerType(_Bounded, integerType):

	min=1
	max=None

class negativeIntegerType(_Bounded, integerType):

	min=None
	max=-1

class unsignedLongType(_Bounded, integerType):

	min=0
	max=18446744073709551615L

class unsignedIntType(_Bounded, integerType):

	min=0
	max=4294967295L

class longType(_Bounded, integerType):

	min = -9223372036854775808L
	max =  9223372036854775807L


class intType(int, _Numeric):
	"""
	"""

class shortType(_Bounded, intType):

	min = -32768
	max =  32767

class unsignedShortType(_Bounded, intType):

	min = 0
	max = 65535

class byteType(_Bounded, intType):

	min = -128
	max =  127

class unsignedByteType(_Bounded, intType):

	min = 0
	max = 255

class decimalType(_Numeric):

	def __init__(self, value):
		value = tokenType(unicode(value))
		if re.match("^[+-]?([0-9]*\.[0-9]+|[0-9]+\.?[0-9]*)$", value):
			try:
				i, d = value.split('.')
			except ValueError:
				i = value
				d=""
			# d = d.rstrip("0")
			d = re.sub("0*$", "", d)
			self.val = long("%s%s" % (i,d))
			self.power = len(d)
			self.value = value
		else:
			raise ValueError

	def __cmp__(self, other):
		if other.__class__ != decimalType:
			other = decimalType(other)
		if self.power < other.power:
			o = other.val
			s = self.val * 10 **  (other.power - self.power)
		else:
			s = self.val
			o = other.val * 10 **  (self.power - other.power)
		return s.__cmp__(o)

	def fractionDigitsFacet(self, value):
		return self.power <= value

	def totalDigitsFacet(self, value):
		if self.val < 0:
			return len(str(self.val)) -1 <= value
		else:
			return len(str(self.val))  <= value
			
class dateType(_Ordered):

	formatWoTZ = "%Y-%m-%d"
	formatZ = "%Y-%m-%dZ"
	
	def __init__(self, value):
		self.lexical = tokenType(value)
		if not re.match("[ \t\n]*[0-9]+-[0-9]{2}-[0-9]{2}Z?", value):
			raise ValueError
		try:
			self.value = time.strptime(value, dateType.formatWoTZ)
			self.tz = 0
		except ValueError:
			self.value = time.strptime(value, dateType.formatZ)
			self.tz = 1
		if self.value[2] == 31 and not self.value[1] in (1, 3, 5, 7, 8, 10, 12):
			raise ValueError
		if self.value[2] == 30 and self.value[1] == 2:
			raise ValueError
		if self.value[2] == 29 and self.value[1] == 2 and ((self.value[0] % 4 != 0) or (self.value[0] % 100 == 0)):
			raise ValueError
	
	def __cmp__(self, other):
		if other.__class__ != dateType:
			other = dateType(other)
		for i in range (0, 5):
			res = self.value[i].__cmp__(other.value[i])
			if res != 0:
				return res
		return 0


