#!/usr/bin/python


import objectify
import os, sys, tempfile
import xml.sax.saxutils, xml.sax
import re

class Variable(objectify._XO_):

	def getFileName(self, outie):
		outie.startElementWithAnnotations("variable", self)
		if not hasattr(self, "fileName"):
			self.fileName = self.transform.getFileName(outie)
		outie.endElement("variable")
		return self.fileName


class Framework(objectify._XO_):

	def __getitem__(self, key):
		for v in self.variable:
			if v.name == key:
				return v
		raise IndexError
		
	def __len__(self):
		return len(self.variable)

class WithParam(objectify._XO_):

	def format(self, outie, type):
		outie.startElementWithAnnotations("with-param", self)
		name = self.name
		value = self.select[1:-1]
		p = eval(type.parameter.PCDATA)
		outie.endElement("with-param")
		return p

class Type(objectify._XO_):

	def isValid(self, stdout, stderr):
		if self.error.flow.PCDATA == "stdout":
			toTest = stdout
		else:
			toTest = stderr
		return re.compile(self.error.regexp.PCDATA).search(toTest) == None

class TypeCollection(objectify._XO_):

	def __getitem__(self, key):
		for type in self.type:
			if type.extension.PCDATA == key:
				return type
		raise IndexError
		
	def __len__(self):
		return len(self.type)

class Schema(objectify._XO_):

	def getFileName(self, outie):
		outie.startElementWithAnnotations("schema", self)
		self.fileName = self.transform.getFileName(outie)
		outie.endElement("schema")
		return self.fileName

class Transform(objectify._XO_):

	def getFileName(self, outie):
		outie.startElementWithAnnotations("transformation", self)
		if not hasattr(self, 'fileName'):
			extension = self.transformation[self.transformation.rindex('.'):]
			transformation = self.transformation
			if hasattr(self, 'instance'):
				instance = self.instance
			else:
				instance = outie.instance
			if hasattr(self, "extension"):
				suffix = self.extension
			else:
				suffix = ".xml"
			result = tempfile.mktemp(suffix)
			type = outie.config.transformations[extension]
			parameters = ""
			if hasattr(self, 'with__param'):
				for p in self.with__param:
					parameters += p.format(outie, type)+" "
			command = eval(type.command.PCDATA)
			(cin, cout, cerr) = os.popen3(command)
			stdout = cout.read()
			stderr = cerr.read()
			outie.element("stdout", stdout)
			outie.element("sterr", stderr)
			self.fileName = result
		outie.endElement("transformation")
		return self.fileName

class Instance(objectify._XO_):
	
	def setFileName(self, fileName):
		self.fileName=fileName

	def getFileName(self, outie):
		outie.startElementWithAnnotations("instance", self)
		if not hasattr(self, "fileName"):
			self.fileName = self.transform.getFileName(outie)
		outie.endElement("instance")
		return self.fileName

class IsValid(objectify._XO_):

	def validate(self, outie, rule):
		outie.startElementWithAnnotations("isValid", self)
		if self.schema.__class__ == Schema:
			schema = self.schema.getFileName(outie)
		else:
			if self.schema[0] == '$':
				schema =  outie.framework[self.schema[1:]].getFileName(outie)
			else:
				schema = self.schema
		extension = schema[schema.rindex('.'):]
		instance = rule.getInstance(outie)
		type = outie.config.schemas[extension]
		command = eval(type.command.PCDATA)
		(cin, cout, cerr) = os.popen3(command)
		stdout = cout.read()
		stderr = cerr.read()
		outie.element("stdout", stdout)
		outie.element("sterr", stderr)
		valid = type.isValid(stdout, stderr)
		outie.element("valid", valid)
		outie.endElement("isValid")
		return valid

class Choice(objectify._XO_):

	def validate(self, outie, rule):
		outie.startElementWithAnnotations("choice", self)
		valid = 0
		if hasattr(self, 'isValid'):
			for iv in self.isValid:
				valid = iv.validate(outie, rule)
				if valid:
					break
		if (not valid) and hasattr(self, 'choice'):
			for c in self.choice:
				valid = c.validate(outie, rule)
				if valid:
					break
		if (not valid) and hasattr(self, 'apply__rules'):
			for ar in self.apply__rules:
				valid = ar.validate(outie, rule)
				if valid:
					break
		outie.element("valid", valid)
		outie.endElement("choice")
		return valid

class Assert(objectify._XO_):

	def validate(self, outie, rule):
		outie.startElementWithAnnotations("assert", self)
		valid = 1
		if hasattr(self, 'isValid'):
			for iv in self.isValid:
				valid = iv.validate(outie, rule)
				if not valid:
					break
		if valid and hasattr(self, 'choice'):
			for c in self.choice:
				valid = c.validate(outie, rule)
				if not valid:
					break
		if valid and hasattr(self, 'apply__rules'):
			for ar in self.apply__rules:
				valid = ar.validate(outie, rule)
				if not valid:
					break
		outie.element("valid", valid)
		outie.endElement("assert")
		return valid

class ApplyRules(objectify._XO_):

	def validate(self, outie, rule):
		outie.startElementWithAnnotations("apply-rules", self)
		valid = 1
		for rule in outie.framework.rule:
			if hasattr(rule, 'mode') and rule.mode==self.mode:
				valid = rule.validate(outie)
				if not valid:
					break
		outie.element("valid", valid)
		outie.endElement("apply-rules")
		return valid

class Rule(objectify._XO_):

	def validate(self, outie):
		outie.startElementWithAnnotations("rule", self)
		for a in self._assert:
			valid = a.validate(outie, self)
			if not valid:
				break
		outie.element("valid", valid)
		outie.endElement("rule")
		return valid

	def getInstance(self, outie):
		if not hasattr(self, 'instance'):
			self.instance = Instance()
			self.instance.setFileName(outie.instance)
		elif self.instance.__class__ != Instance:
			if self.instance[0] == '$':
				return outie.framework[self.instance[1:]].getFileName(outie)
			else:
				return self.instance
		return self.instance.getFileName(outie)

class Outie:

	def __init__(self, filename, instance):
		objectify._XO_type = Type
		objectify._XO_schemas = TypeCollection
		objectify._XO_transformations = TypeCollection
		objectify._XO_instance = Instance
		objectify._XO_transform = Transform
		objectify._XO_variable = Variable
		objectify._XO_framework = Framework
		self.config = objectify.XML_Objectify('/home/vdv/xmlschemata-cvs/downloads/python/xvif/outie/config.xml').make_instance()
		objectify._XO_rule = Rule
		objectify._XO__assert = Assert
		objectify._XO_isValid = IsValid
		objectify._XO_choice = Choice
		objectify._XO_schema = Schema
		objectify._XO_apply__rules = ApplyRules
		objectify._XO_with__param = WithParam
		self.framework = objectify.XML_Objectify(filename).make_instance()
		self.instance = instance
		self.oh = xml.sax.saxutils.XMLGenerator(sys.stdout, "utf-8")
		self.noAtt=xml.sax.saxutils.AttributeMap({})
	
	def validate(self):
		self.oh.startDocument()
		self.startElementWithAnnotations("framework", self.framework)
		valid = 1
		for rule in self.framework.rule:
			if not hasattr(rule, 'mode') or rule.mode=="":
				valid = rule.validate(self)
				if not valid:
					break
		self.element("valid", valid)
		self.endElement("framework")
		self.oh.endDocument()
		return valid
	
	def startElementWithAnnotations(self, name, node):
		atts = {}
		for att in ('id', 'name', 'mode', 'schema', 'transformation', 'instance'):
			if hasattr(node, att) and getattr(node, att).__class__.__name__=="unicode":
				atts[att] = getattr(node, att)
		atts=xml.sax.saxutils.AttributeMap(atts) 
		self.oh.startElement(name, atts)
		self.oh.characters("\n")
		if hasattr(node, 'documentation'):
			for d in node.documentation:
				self.element("documentation", d.PCDATA)
			
	def startElement(self, name, atts=None, cr=1):
		if atts==None:
			atts = self.noAtt;
		self.oh.startElement(name, atts)
		if cr:
			self.oh.characters("\n")
			
	def endElement(self, name, cr=1):
		self.oh.endElement(name)
		if cr:
			self.oh.characters("\n")
			
	def characters(self, str):
		self.oh.characters(str.__str__())
	
	def element(self, name, value, atts=None, cr=1):
		self.startElement(name, atts, 0)
		self.characters(value)
		self.endElement(name)
			
if __name__ == "__main__":
	outie = Outie(sys.argv[1], sys.argv[2])
	valid = outie.validate()
	sys.exit(not valid)
