# coding: utf-8

'''
Created on 12.11.2014

@author: tr0glo)|(I╠╣
'''



import re, os
import json

from common.sysfunctions import toHexForXml
from workflow.processUtils import ActivitiObject
from ru.curs.celesta.showcase.utils import XMLJSONConverter
from workflow.processUtils import ActivitiObject, getBase64Image


from workflow._workflow_orm import matchingCircuitCursor, statusCursor, statusTransitionCursor

try:
    from ru.curs.showcase.core.jython import JythonDTO
except:
    from ru.curs.celesta.showcase import JythonDTO
    
try:
    from ru.curs.showcase.runtime import AppInfoSingleton
except:
    pass

from org.xml.sax.helpers import XMLReaderFactory
from org.xml.sax.ext import DefaultHandler2
from org.xml.sax import InputSource
from  javax.xml.stream import XMLOutputFactory
from java.io import FileInputStream, StringWriter, ByteArrayInputStream
from java.lang import String
import javax.xml.stream.XMLInputFactory as XMLInputFactory
import java.io.InputStreamReader as InputStreamReader
from org.activiti.engine.impl.util.io import InputStreamSource
from org.activiti.engine.impl.util.io import StreamSource
import org.activiti.bpmn.BpmnAutoLayout as BpmnAutoLayout
import org.activiti.bpmn.converter.BpmnXMLConverter as BpmnXMLConverter

def webtextData(context, main=None, add=None, filterinfo=None,
             session=None, elementId=None):
#     activiti = ActivitiObject()
#     taskService = activiti.taskService;
#  
#     session = json.loads(session)['sessioncontext']
#     drawInstance = False
#     drawProcess = False
#     for params in session['urlparams']['urlparam']:
#         if params['@name'] == 'processId':
#             procInstId = params['@value'][0]
#             drawInstance = True
#         if params['@name'] == 'processKey':
#             procKey = params['@value'][0]
#             drawProcess = True
#     if drawInstance:
#         data = {"image":{"@align":"center",
#                      "@src": u"data:image/png;base64," + getBase64Image(activiti.getExecutionModel(procInstId))}}
#     elif drawProcess:
#         data = {"image":{"@align":"center",
#                      "@src": u"data:image/png;base64," + getBase64Image(activiti.getDeployedProcessModel(procKey))}}        
#          
    session = json.loads(session)
    matchingCircuit = matchingCircuitCursor(context)
    matchingCircuitClone = matchingCircuitCursor(context)
    processKey = session['sessioncontext']['related']['xformsContext']['formData']['schema']['data']['@processKey']
    processName = session['sessioncontext']['related']['xformsContext']['formData']['schema']['data']['@processName']
    matchingCircuit.setRange('processKey',processKey)
    if matchingCircuit.count() == 0:
        data = {'div':u'В процессе не задано задач'}
    else:  
        matchingCircuitClone.setRange('processKey',processKey)
        matchingCircuit.setRange('type','parallel')
        parallelFlag = True
        taskFlag = True
        maxParallelTasks = 1
        #Проверка на то, что в каждом параллельном согласовании не менее двух задач
        for matchingCircuit in matchingCircuit.iterate():
            matchingCircuitClone.setFilter('number',"'%s.'%%" % matchingCircuit.number)
            if matchingCircuitClone.count() < 2:
                parallelFlag = False
            if matchingCircuitClone.count() > maxParallelTasks:
                maxParallelTasks = matchingCircuitClone.count()
        matchingCircuit.clear()
        matchingCircuitClone.clear()
        matchingCircuit.setRange('processKey',processKey)
        matchingCircuit.setFilter('number',"!%'.'%")
        matchingCircuit.orderBy('sort')
        matchingCircuitClone.setRange('processKey',processKey)
        if parallelFlag and taskFlag:#Схема согласования корректна
#             processXML = getProcessXML(context,matchingCircuit,matchingCircuitClone, processKey, processName)
#             data = {'div':processXML}
            actObj = ActivitiObject()
            #raise Exception(getProcessXML(context,matchingCircuit,matchingCircuitClone, processKey, processName).encode('utf-8'))
            #Получение xml-описания процесса
            processDefinition = getProcessXML(context,matchingCircuit,matchingCircuitClone, processKey, processName, maxParallelTasks)
            stream = ByteArrayInputStream(processDefinition.encode('utf-8'))
            #Генерация картинки процесса
            xmlSource = InputStreamSource(stream)
            model = BpmnXMLConverter().convertToBpmnModel(xmlSource, False, False, String('UTF-8'))
            actObj.repositoryService.validateProcess(model)
            #BpmnAutoLayout(model).execute()
            generator = actObj.conf.getProcessDiagramGenerator()
            imageStream = generator.generatePngDiagram(model)
            #a1,a2 = getProcessXML(context,matchingCircuit,matchingCircuitClone, processKey, processName,maxParallelTasks)
            #data = {'div':[a1,a2]}
            data = {"image":{"@align":"center",
                       "@src": u"data:image/png;base64," + getBase64Image(imageStream)}}        

        elif not parallelFlag:
            data = {'div':u'В одном из параллельных согласований содержится меньше двух элементов'}
        else:
            data = {'div':u'В процессе есть задачи с неназначенными статусами'}
    return JythonDTO(XMLJSONConverter.jsonToXml(json.dumps(data)), None)


def getProcessXML(context,matchingCircuit,matchingCircuitClone, processKey, processName, maxParallelTasks):
    u'''Функция генерации XML-описания процесса'''
    try:
        #rootPath = AppInfoSingleton.getAppInfo().getCurUserData().getPath() + '/xforms/workflow/'
        
        rootPath = os.path.dirname(os.path.abspath(__file__)) + '/'
        
        #rootPath = 'C:/jprojects/celesta/manage/general/xforms/workflow/'
    except:
        rootPath = 'C:/jprojects/celesta/manage/general/xforms/workflow/'
    #raise Exception(rootPath,type(rootPath))
    #Пути к шаблонам частей описания процесса
    startAndEndPath = rootPath + 'typicalProcessTemplate.bpmn.xml' #путь к блоку со стартом и концом описания процесса
    consecPath = rootPath + 'consecutiveTaskTemplate.bpmn.xml' #путь к блоку, описывающему последовательную задачу
    parallelMatchingPath = rootPath + 'parallelMatchingTemplate.bpmn.xml' #путь к блоку, описываюшему параллельное выполнение задач
    parallelTaskPath  = rootPath + 'parallelTaskTemplate.bpmn.xml' #путь к блоку, описываюшему параллельную задачу
    #Объявление переменных
    status = statusCursor(context)
    statusTransition = statusTransitionCursor(context)
    stringWriter = StringWriter()
    diagramWriter = StringWriter()
    xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(stringWriter)
    xmlDiagramWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(diagramWriter)
    parser = XMLReaderFactory.createXMLReader()
    handler = XformsProcessTemplate(startAndEndPath, consecPath, parallelMatchingPath,
                                    parallelTaskPath, matchingCircuit,
                                    matchingCircuitClone,processKey,
                                    processName, xmlWriter,
                                    xmlDiagramWriter, diagramWriter,
                                    maxParallelTasks, status,
                                    statusTransition)
    parser.setContentHandler(handler)
    parser.setErrorHandler(handler)
    parser.setFeature("http://xml.org/sax/features/namespace-prefixes", True)
    parser.setProperty("http://xml.org/sax/properties/lexical-handler", handler)
    #Парсинг шаблона
    stream = FileInputStream(startAndEndPath)
    parser.parse(InputSource(stream))
    xmlWriter.close()
    stringWriter.close()
    diagramWriter.close()
    xmlDiagramWriter.close()
    stream.close()
    return stringWriter.toString()

def transformToVar(exp,var):
    u'''Функция трансформации переменных в вид, понятный activiti'''
    if exp.match(var):
        var = var.replace('[','{')
        var = var.replace(']','}')
        return var
    else:
        return var
    
def extractAssigneeAndCandidates(assJSON):
    u'''Получение ответственного за задачу и кандидатов'''
    pattern = '\$\[\w+\]'
    exp = re.compile(pattern)
    assJSON = json.loads(assJSON)
    assignee = transformToVar(exp,assJSON['assignee'])
    userList = list()
    for user in assJSON['users']:
        userList.append(transformToVar(exp,user['id']))
    groupList = list()
    for group in assJSON['groups']:
        groupList.append(transformToVar(exp,group['id']))
    return assignee, ','.join(userList),','.join(groupList)
    
def findAndReplacePattern(ch,start,length,rep,pattern,xmlWriter):
    u'''Функция для замены паттерна на другую строку'''
    flag = True
    if len(pattern) == length:
        for i in range(length):
            if ch[start+i] != pattern[i]:
                flag = False
    else:
        flag = False
    if flag:
        xmlWriter.writeCharacters(rep, 0, len(rep))
        return True
    else:
        return False 
    
def getStatusAndTransitionVal(status,statusTransition):
    statusValue = json.dumps([status.id,status.varName])
    statusTransition.setRange('statusFrom',status.id)
    statusTransition.setRange('modelFrom',status.modelId)
    transitionValue = dict()
    for statusTransition in statusTransition.iterate():
        status.get(statusTransition.statusTo,statusTransition.modelTo)
        transitionValue[status.id] = status.varName
    transitionValue = json.dumps(transitionValue)
    return statusValue,transitionValue   
    
def addBPMNShape(id, height, width, x, y, diagramWriter):
    u'''Функция записи в xml-файл объекта процесса по id с заданной шириной, высотой и координатами'''
    diagramWriter.writeStartElement('bpmndi:BPMNShape')
    diagramWriter.writeAttribute('bpmnElement',id)
    diagramWriter.writeAttribute('id','BPNMShape_'+id)
    diagramWriter.writeStartElement('omgdc:Bounds')
    diagramWriter.writeAttribute('height',str(height))
    diagramWriter.writeAttribute('width',str(width))
    diagramWriter.writeAttribute('x',str(x))
    diagramWriter.writeAttribute('y',str(y))
    diagramWriter.writeEndElement()                                                                                       
    diagramWriter.writeEndElement()
    return y+height

def addBPMNEdge(id, points, diagramWriter):
    u'''Функция записи в xml-файл ребра по заданным точкам'''
    diagramWriter.writeStartElement('bpmndi:BPMNEdge')
    diagramWriter.writeAttribute('bpmnElement',id)
    diagramWriter.writeAttribute('id','BPMNEdge_'+id)
    for point in points:
        diagramWriter.writeStartElement('omgdi:waypoint')
        diagramWriter.writeAttribute('x',str(point[0]))
        diagramWriter.writeAttribute('y',str(point[1]))
        diagramWriter.writeEndElement()  
    diagramWriter.writeEndElement()

def addSpecialTag(writer):
    u'''Функция записи в xml-файл специального тэга'''
    writer.writeStartElement('specialTag')
    writer.writeAttribute('xmlns','http://www.omg.org/spec/BPMN/20100524/MODEL')
    writer.writeAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance')
    writer.writeAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema')
    writer.writeAttribute('xmlns:activiti','http://activiti.org/bpmn')
    writer.writeAttribute('xmlns:bpmndi','http://www.omg.org/spec/BPMN/20100524/DI')
    writer.writeAttribute('xmlns:omgdc','http://www.omg.org/spec/DD/20100524/DC')
    writer.writeAttribute('xmlns:omgdi','http://www.omg.org/spec/DD/20100524/DI')
    writer.writeAttribute('typeLanguage','http://www.w3.org/2001/XMLSchema')
    writer.writeAttribute('expressionLanguage','http://www.w3.org/1999/XPath')
    writer.writeAttribute('targetNamespace','http://activiti.org/bpmn20')
    writer.writeAttribute('id','specTa')
    
    
class XformsProcessTemplate(DefaultHandler2):
    u'''SAX-parser для описания процесса старта и конца процесса'''
    def __init__(self, startAndEndPath, consecPath, parallelMatchingPath,\
                 parallelTaskPath,matchingCircuit,matchingCircuitClone, processKey,\
                 processName,xmlWriter, xmlDiagramWriter,diagramWriter, maxParallelTasks,
                 status, transition):
        #Пути к шаблонам процессов
        self.startAndEndPath = startAndEndPath
        self.consecPath = consecPath
        self.parallelMatchingPath = parallelMatchingPath
        self.parallelTaskPath = parallelTaskPath
        #Xml-writerы для описания процесса и описания диаграмы
        self.xmlWriter = xmlWriter
        self.diagramWriter = diagramWriter
        self.xmlDiagramWriter = xmlDiagramWriter
        #Курсор таблицы схемы согласования
        self.matchingCircuit = matchingCircuit
        self.matchingCircuitClone = matchingCircuitClone
        self.processKey = processKey
        self.processName = processName
        self.maxParallelTasks = maxParallelTasks
        #Инициализация параметров объктов диаграммы
        self.startY = 30  #Начальная ордината
        self.defaultStartEndDiametr = 35 #Размер круга старта и конца процессов
        self.gatewayAxis = 40 #Размер шлюза
        self.defaultTaskHeight = 55 # Высота задачи
        self.defaultTaskWidth = 300 # Ширина задачи
        self.defaultFlowLength = 75 # Длина ребра
        #Начальная абсцисса
        self.startX = ((self.defaultTaskWidth)*(self.maxParallelTasks+1)+self.gatewayAxis*(self.maxParallelTasks-1))/2
        #Текущее положение абсциссы
        self.currentY = int()
        #Курсоры статусов и соединений между статусами для генерации значений сответствющих переменных
        self.status = status
        self.statusTransition = transition
        

    def startDocument(self):
        self.xmlWriter.writeStartDocument("UTF-8", "1.0")

    def endDocument(self):
        self.xmlWriter.writeEndDocument()
        self.xmlWriter.flush()

    def startElement(self, namespaceURI, lname, qname, attrs):
        #Тег не описание задач, процесса, описание диаграммы
        if qname != 'startDescriptionTasks' and qname != 'process' and qname != 'diagramDescription':#Обыные элементы просто переписываем
            self.xmlWriter.writeStartElement(qname)
            #Запись атрибутов, для корневого тэга диаграммы подменяем ключ процесса
            for i in range(0, attrs.getLength()):
                if qname == 'bpmndi:BPMNDiagram' and attrs.getQName(i) == 'id':
                    self.xmlWriter.writeAttribute(attrs.getQName(i), 'BPMNDiagram_'+self.processKey)
                elif qname == 'bpmndi:BPMNPlane' and attrs.getQName(i) == 'bpmnElement':
                    self.xmlWriter.writeAttribute(attrs.getQName(i), self.processKey)
                elif qname == 'bpmndi:BPMNPlane' and attrs.getQName(i) == 'id':
                    self.xmlWriter.writeAttribute(attrs.getQName(i), 'BPMNPlane_'+self.processKey)
                else:    
                    self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i))
            if qname == 'startEvent':#Добавление в диаграмму начального события
                self.currentY = addBPMNShape("startevent1",
                                      self.defaultStartEndDiametr,
                                      self.defaultStartEndDiametr,
                                      self.startX-self.defaultStartEndDiametr/2,
                                      self.startY-self.defaultStartEndDiametr/2,
                                      self.xmlDiagramWriter)
            elif qname == 'userTask':
                if attrs.getValue('id') == "createDocument":#Добавление в диаграмму Создания документа
                    self.currentY = addBPMNShape("createDocument",
                                      self.defaultTaskHeight,
                                      self.defaultTaskWidth,
                                      self.startX-self.defaultTaskWidth/2,
                                      self.startY+self.defaultStartEndDiametr/2+self.defaultFlowLength,
                                      self.xmlDiagramWriter)
                elif attrs.getValue('id') == "reworkDocument":# Добавление Доработки документа
                    self.currentY = addBPMNShape("reworkDocument",
                                      self.defaultTaskHeight,
                                      self.defaultTaskWidth,
                                      self.startX*2 - self.defaultTaskWidth/2,
                                      self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+0.5*self.defaultTaskHeight+self.gatewayAxis/2,
                                      self.xmlDiagramWriter)
            elif qname == 'endEvent':
                if attrs.getValue('id') == "endevent1":# Первый выход из процесса по удалению документа
                    self.currentY = addBPMNShape("endevent1",
                                      self.defaultStartEndDiametr,
                                      self.defaultStartEndDiametr,
                                      self.startX-self.defaultTaskWidth/2-self.defaultStartEndDiametr,
                                      self.startY+2*self.defaultFlowLength+self.defaultTaskHeight+self.defaultStartEndDiametr/2,
                                      self.xmlDiagramWriter)                              
                elif attrs.getValue('id') == "endevent2":# Второй выход из процесса по его завершению
                    self.currentY = addBPMNShape("endevent2",
                                      self.defaultStartEndDiametr,
                                      self.defaultStartEndDiametr,
                                      self.startX-self.defaultStartEndDiametr/2,
                                      self.currentY+self.defaultFlowLength,
                                      self.xmlDiagramWriter)
            elif qname == 'exclusiveGateway':# Шлюз на удаление документа
                self.currentY = addBPMNShape("deleteDocumentExclusivegateway",
                                  self.gatewayAxis,
                                  self.gatewayAxis,
                                  self.startX-self.gatewayAxis/2,
                                  self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+self.defaultTaskHeight,
                                  self.xmlDiagramWriter)
            elif qname == 'sequenceFlow':
                if attrs.getValue('id') == "fromStartToCreate":# Ребо от старта к созданию документа
                    addBPMNEdge("fromStartToCreate",
                                 [ (self.startX, self.startY+self.defaultStartEndDiametr/2),
                                   (self.startX, self.startY+self.defaultStartEndDiametr/2+self.defaultFlowLength)],
                                  self.xmlDiagramWriter)                   
                elif attrs.getValue('id') == "fromStartToDeleteGateway":# Ребро от Создания документа к щлюзу по удалению
                    addBPMNEdge("fromStartToDeleteGateway",
                                 [ (self.startX, self.startY+self.defaultStartEndDiametr/2+self.defaultFlowLength+self.defaultTaskHeight),
                                   (self.startX, self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+self.defaultTaskHeight)],
                                  self.xmlDiagramWriter)
                elif attrs.getValue('id') == "fromDeleteToEnd1":#Ребро ищ шлюза к первому выходу из процесса
                    addBPMNEdge("fromDeleteToEnd1",
                                 [ (self.startX, self.startY+self.defaultStartEndDiametr/2),
                                   (self.startX, self.startY+self.defaultStartEndDiametr/2+self.defaultFlowLength)],
                                  self.xmlDiagramWriter)                             
                elif attrs.getValue('id') == "fromReworkToDelete":# Ребро от доработки процесса к щлюзу по удалению
                    addBPMNEdge("fromReworkToDelete",
                                 [(self.startX*2 - self.defaultTaskWidth/2,
                                    self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+self.defaultTaskHeight+self.gatewayAxis/2),
#                                   (self.startX*2-self.defaultStartEndDiametr - self.defaultTaskWidth/2,
#                                    self.startY+self.defaultStartEndDiametr/2+self.defaultFlowLength+self.defaultTaskHeight/2),
#                                   (self.startX*2-self.defaultStartEndDiametr - self.defaultTaskWidth/2,
#                                     self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+self.defaultTaskHeight+self.gatewayAxis/2),
                                  (self.startX,
                                   self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+self.defaultTaskHeight+self.gatewayAxis/2),  
                                   ],
                                  self.xmlDiagramWriter)
                elif attrs.getValue('id') == "finalFlow":#Ребро из последнего согласования ко второму выходу из процесса
                    addBPMNEdge("finalFlow",
                                 [ (self.startX, self.currentY),
                                   (self.startX, self.currentY+self.defaultFlowLength)],
                                  self.xmlDiagramWriter)           
        elif qname == 'process':#Подменяем название и ключ процесса
            self.xmlWriter.writeStartElement(qname)
            addSpecialTag(self.xmlDiagramWriter)
            for i in range(0, attrs.getLength()):
                if attrs.getQName(i) == 'id':
                    self.xmlWriter.writeAttribute(attrs.getQName(i), self.processKey)
                elif attrs.getQName(i) == 'name':
                    self.xmlWriter.writeAttribute(attrs.getQName(i), self.processName)
                else:
                    self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i))
        elif qname == 'startDescriptionTasks':#Начало описания задач процесса
            inGatewayId = 'deleteDocumentExclusivegateway'
            self.matchingCircuitClone.setFilter('number',"!%'.'%")
            topTasksCount = self.matchingCircuitClone.count()
            self.matchingCircuitClone.setRange('number')
            counter = 0
            parallelGatewayCounter = 1
            self.currentY = self.startY + self.defaultFlowLength * 2 + self.defaultTaskHeight + self.gatewayAxis + self.defaultStartEndDiametr/2
            for matchingCircuit in self.matchingCircuit.iterate():
                if matchingCircuit.type == 'task':#Обработка последовательной задачи
                    counter += 1
                    #если задача последняя
                    if topTasksCount == counter:
                        outGatewayId = 'finalApprovementExclusivegateway'
                    else:
                        outGatewayId = 'outGateway'+str(matchingCircuit.id)
                    #Получение ответственных
                    assignee,candidates,groups = extractAssigneeAndCandidates(matchingCircuit.assJSON)
                    
                    statusVal, transitionsVal = '',''
                    consecParser = XMLReaderFactory.createXMLReader()
                    consecHandler = consecWriter(inGatewayId,matchingCircuit.taskKey,
                                                 matchingCircuit.name, assignee,
                                                 candidates,groups,outGatewayId,
                                                 self.xmlWriter,self.xmlDiagramWriter,
                                                 self.currentY, self.startX,
                                                 statusVal,transitionsVal)
                    #Изменям текущую абсциссу, добавляя к ней высоту описания последовательной задачи
                    self.currentY = self.currentY + 2*self.defaultFlowLength + self.defaultTaskHeight + self.gatewayAxis
                    consecParser.setContentHandler(consecHandler)
                    consecParser.setErrorHandler(consecHandler)
                    consecParser.setFeature("http://xml.org/sax/features/namespace-prefixes", True)
                    consecParser.setProperty("http://xml.org/sax/properties/lexical-handler", consecHandler)
                    #Парсинг шаблона описания последовательной задачи
                    stream = FileInputStream(self.consecPath)
                    consecParser.parse(InputSource(stream))
                    #id выходного шлюза есть id входного шлюза для следующего элемента
                    inGatewayId = outGatewayId
                    #raise Exception(consecHandler.char)
                else:#Обработка параллельного согласования
                    self.matchingCircuitClone.setFilter('number',"'%s.'%%" % matchingCircuit.number)
                    counter += 1
                    #если задача последняя
                    if topTasksCount == counter:
                        outGatewayId = 'finalApprovementExclusivegateway'
                    else:
                        outGatewayId = 'outGatewayParallel'+str(counter)
                    parallelParser = XMLReaderFactory.createXMLReader()
                    # счётчики для идентификаторов шлюзов
                    parallelGatewayIn = parallelGatewayCounter
                    parallelGatewayCounter += 1
                    parallelGatewayOut = parallelGatewayCounter
                    parallelGatewayOut += 1
                    parallelId = parallelGatewayCounter
                    parallelGatewayCounter += 1
                    parallelHandler = parallelWriter(inGatewayId, parallelGatewayIn,
                                                     parallelGatewayOut,
                                                     self.matchingCircuitClone, outGatewayId,
                                                     parallelId,self.parallelTaskPath,self.xmlWriter,
                                                     self.xmlDiagramWriter,
                                                     self.startX, self.currentY,
                                                     self.status, self.statusTransition)
                    #обновление  текущей абсциссы
                    self.currentY = self.currentY + 2*self.defaultFlowLength + self.defaultTaskHeight + 7*self.gatewayAxis
                    parallelParser.setContentHandler(parallelHandler)
                    parallelParser.setErrorHandler(parallelHandler)
                    parallelParser.setFeature("http://xml.org/sax/features/namespace-prefixes", True)
                    parallelParser.setProperty("http://xml.org/sax/properties/lexical-handler", parallelHandler)
                    stream = FileInputStream(self.parallelMatchingPath)
                    #парсинг шаблона параллельных задач
                    parallelParser.parse(InputSource(stream))
                    inGatewayId = outGatewayId
        elif qname == 'diagramDescription':# вставка описания диаграммы
            diagramParser = XMLReaderFactory.createXMLReader()
            diagramHandler = diagramWriter(self.xmlWriter)
            diagramParser.setContentHandler(diagramHandler)
            diagramParser.setErrorHandler(diagramHandler)
            diagramParser.setFeature("http://xml.org/sax/features/namespace-prefixes", True)
            diagramParser.setProperty("http://xml.org/sax/properties/lexical-handler", diagramHandler)
            stream = ByteArrayInputStream(self.diagramWriter.toString().encode('utf-8'))
            #парсим описание диаграммы и вставляем его в общий xml
            diagramParser.parse(InputSource(stream))
    

    def endElement(self, uri, lname, qname):
        #специальные теги-маркеры не пишутся в окончательный файл
        if qname != 'startDescriptionTasks' and qname != 'diagramDescription':
            if qname == 'process':#при закрытии тэга процесса закрываем writer диаграммы
                self.xmlDiagramWriter.writeEndElement()
                self.diagramWriter.close()
                self.xmlDiagramWriter.close
            self.xmlWriter.writeEndElement()

    def comment(self, ch, start, length):
        self.xmlWriter.writeComment(''.join(ch[start:start + length]))

    def startPrefixMapping(self, prefix, uri):
        if prefix == "":
            self.xmlWriter.setDefaultNamespace(uri)
        else:
            self.xmlWriter.setPrefix(prefix, uri)


    def characters(self, ch, start, length):
        self.xmlWriter.writeCharacters(ch, start, length)

    def processingInstruction(self, target, data):
        self.xmlWriter.writeProcessingInstruction(target, data)

    def skippedEntity(self, name):
        self.xmlWriter.writeEntityRef(name)


class consecWriter(DefaultHandler2):
    u'''SAX-parser для блока последовательной задачи'''
    def __init__(self,inGatewayId,taskId,taskName, assignee,\
                  candidates, groups, outGatewayId,xmlWriter,\
                  xmlDiagramWriter, currentY, startX, statusVal, transitionsVal):
        #инициализация идентификатора входного шлюза
        self.inGatewayId = inGatewayId
        #ответственные
        self.assignee = assignee
        self.candidates = candidates
        self.groups = groups
        #задача и ее названия
        self.taskId = taskId
        self.taskName = taskName
        #идентификатор выходного шлюза
        self.outGatewayId = outGatewayId
        self.xmlWriter = xmlWriter
        self.xmlDiagramWriter = xmlDiagramWriter
        #параметры диаграммы, аналогичные параметры в парсере общего шаблона
        self.currentY = currentY
        self.startX = startX
        self.startY = 30 
        self.defaultStartEndDiametr = 35
        self.gatewayAxis = 40
        self.defaultTaskHeight = 55
        self.defaultTaskWidth = 300
        self.defaultFlowLength = 75
        #Значение статуса и переходов
        self.statusVal = statusVal
        self.transitionsVal = transitionsVal

    def startElement(self, namespaceURI, lname, qname, attrs):
        if qname != "specialTag":#спецтег не пишется
            self.xmlWriter.writeStartElement(qname)
            if qname == 'userTask':#Описание задачи процесса             
                for i in range(0, attrs.getLength()):
                    if attrs.getQName(i) == 'id':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskId)
                    elif attrs.getQName(i) == 'name':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskName)
                    elif attrs.getQName(i) == 'activiti:assignee':
                        if self.assignee != '':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.assignee)
                    elif attrs.getQName(i) == 'activiti:candidateUsers':
                        if self.candidates != '':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.candidates)
                    elif attrs.getQName(i) == 'activiti:candidateGroups':
                        if self.groups != '':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.groups)
                    else:
                        self.xmlWriter.writeAttribute(attrs.getQName(i),attrs.getValue(i))
                self.currentY = addBPMNShape(self.taskId,
                              self.defaultTaskHeight,
                              self.defaultTaskWidth,
                              self.startX-self.defaultTaskWidth/2,
                              self.currentY,
                              self.xmlDiagramWriter)
            elif qname == 'sequenceFlow':#Описание рёбер
                id = attrs.getValue('id')
                if id == 'inFlow':#Входящее ребро
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'inFlow'+self.taskId)
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.inGatewayId)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskId)
                    addBPMNEdge('inFlow'+self.taskId,
                                 [(self.startX, self.currentY),
                                 (self.startX, self.currentY+self.defaultFlowLength)],
                                 self.xmlDiagramWriter)
                    self.currentY = self.currentY+self.defaultFlowLength
               
                elif id == 'outFlow':#Исходящее ребро
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'outFlow'+self.taskId)
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskId)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.outGatewayId)
                    addBPMNEdge('outFlow'+self.taskId,
                                 [(self.startX, self.currentY+self.defaultTaskHeight),
                                   (self.startX, self.currentY+self.defaultTaskHeight+self.defaultFlowLength)],
                                self.xmlDiagramWriter)
                    self.currentY = self.currentY+self.defaultFlowLength
                
                elif id == 'reworkFlow':#Ребро к доработк процесса
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'reworkFlow'+self.taskId)
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.outGatewayId)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'reworkDocument')
                    addBPMNEdge('reworkFlow'+self.taskId, 
                                [(self.startX+self.gatewayAxis/2, self.currentY-self.gatewayAxis/2),
                                (self.startX*2, self.currentY-self.gatewayAxis/2),
                                (self.startX*2,self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+1.5*self.defaultTaskHeight+self.gatewayAxis/2)],
                                self.xmlDiagramWriter)            
            elif qname == 'exclusiveGateway':#выходной шлюз
                for i in range(0, attrs.getLength()):
                    if attrs.getQName(i) == 'id':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), self.outGatewayId)
                    if attrs.getQName(i) == 'name':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), 'ExclusiveGateway')
                self.currentY = addBPMNShape(self.outGatewayId,
                                      self.gatewayAxis,
                                      self.gatewayAxis,
                                      self.startX-self.gatewayAxis/2,
                                      self.currentY,
                                      self.xmlDiagramWriter)
            else:#Остальные элементы
                for i in range(0, attrs.getLength()):
                    self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i))


    def endElement(self, uri, lname, qname):
        if qname != "specialTag":
            self.xmlWriter.writeEndElement()

    def characters(self, ch, start, length):
        u'''Для первой задачи надо изменить условия выхода из шлюза'''
        if self.inGatewayId == 'deleteDocumentExclusivegateway':
            rep = '${deleteDocument == "false"}'
            pattern = list('${orderApproved == "true"}')
            flowFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
            rep = "task.setVariableLocal('status', '%s')" % self.statusVal
            pattern = list('task.setVariableLocal("status", "createStatus")')
            statusFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
            rep = "task.setVariableLocal('transitions', '%s')" % self.transitionsVal
            pattern = list('task.setVariableLocal("transitions", "createTransitions")')
            transitionsFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
            if not flowFlag and not statusFlag and not transitionsFlag:
                self.xmlWriter.writeCharacters(ch, start, length)
        else:
            rep = "task.setVariableLocal('status', '%s')" % self.statusVal
            pattern = list('task.setVariableLocal("status", "createStatus")')
            statusFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
            rep = "task.setVariableLocal('transitions', '%s')" % self.transitionsVal
            pattern = list('task.setVariableLocal("transitions", "createTransitions")')
            transitionsFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
            if not statusFlag and not transitionsFlag:
                self.xmlWriter.writeCharacters(ch, start, length)

    def comment(self, ch, start, length):
        self.xmlWriter.writeComment(''.join(ch[start:start + length]))

    def startPrefixMapping(self, prefix, uri):
        if prefix == "":
            self.xmlWriter.setDefaultNamespace(uri)
        else:
            self.xmlWriter.setPrefix(prefix, uri)

    def processingInstruction(self, target, data):
        self.xmlWriter.writeProcessingInstruction(target, data)

    def skippedEntity(self, name):
        self.xmlWriter.writeEntityRef(name)
        

class parallelWriter(DefaultHandler2):
    u'''SAX-parser для параллельного блока'''
    def __init__(self,inGatewayId, parallelGatewayIn, parallelGatewayOut,
                  matchingCircuit, outGatewayId, parallelId,
                  parallelTaskPath,xmlWriter, xmlDiagramWriter, startX, currentY,
                  status, transition):
        #идентификаторы входного шлюза, выходного шлюза, входного и выходного шлбза паралелльного согласования
        self.inGatewayId = inGatewayId
        self.parallelGatewayIn = parallelGatewayIn
        self.parallelGatewayOut = parallelGatewayOut
        self.parallelTaskPath = parallelTaskPath
        self.outGatewayId = outGatewayId
        self.parallelId = parallelId
        #курсор схемы согласовния процесса
        self.matchingCircuit = matchingCircuit
        #параметры диаграммы
        self.startX = startX
        self.startY = 30 
        self.defaultStartEndDiametr = 35
        self.gatewayAxis = 40
        self.defaultTaskHeight = 55
        self.defaultTaskWidth = 300
        self.defaultFlowLength = 75
        self.currentY = currentY
        self.xmlDiagramWriter = xmlDiagramWriter
        self.xmlWriter = xmlWriter
        #Курсоры для статусов и переходов
        self.status = status
        self.statusTransition = transition

    def startElement(self, namespaceURI, lname, qname, attrs):
        if qname != "specialTag" and qname != "parallelTasksDescription":#специальные теги не пишем
            self.xmlWriter.writeStartElement(qname)
            if qname == 'parallelGateway':
                id = attrs.getValue('id')
                if id == 'parallelGatewayIn':#Входной шлюз параллельного согласования
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'parallelGatewayIn' + str(self.parallelGatewayIn))
                        if attrs.getQName(i) == 'name':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'paralleGatewayIn')
                    self.currentY = addBPMNShape('parallelGatewayIn' + str(self.parallelGatewayIn),
                                                  self.gatewayAxis,
                                                  self.gatewayAxis,
                                                  self.startX-self.gatewayAxis/2,
                                                  self.currentY,
                                                  self.xmlDiagramWriter)                 
                elif id == 'parallelGatewayOut':#Выходной шлюз параллельного согласования              
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'parallelGatewayOut' + str(self.parallelGatewayOut))
                        if attrs.getQName(i) == 'name':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'paralleGatewayOut')
                    self.currentY = addBPMNShape('parallelGatewayOut' + str(self.parallelGatewayOut),
                              self.gatewayAxis,
                              self.gatewayAxis,
                              self.startX-self.gatewayAxis/2,
                              self.currentY-self.gatewayAxis/2,
                              self.xmlDiagramWriter)                 
            elif qname == 'sequenceFlow':
                id = attrs.getValue('id')
                if id == 'inFlow':#входное ребро во входной паралелльный шлюз
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'inFlowParallel'+str(self.parallelId))
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.inGatewayId)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'parallelGatewayIn' + str(self.parallelGatewayIn))
                    addBPMNEdge('inFlowParallel'+str(self.parallelId),
                                 [(self.startX, self.currentY),
                                 (self.startX, self.currentY+self.defaultFlowLength)],
                                 self.xmlDiagramWriter)
                    self.currentY = self.currentY + self.defaultFlowLength              
                elif id == 'parallelTOexclusive':# ребро из выходного параллельного шлюза к выходному шлюзу
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'outFlowParallel'+str(self.parallelId))
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'parallelGatewayOut' + str(self.parallelGatewayOut))
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.outGatewayId)
                    addBPMNEdge('outFlowParallel'+str(self.parallelId),
                                 [(self.startX, self.currentY),
                                 (self.startX, self.currentY+self.defaultFlowLength)],
                                 self.xmlDiagramWriter)
                    self.currentY = self.currentY + self.defaultFlowLength
                elif id == 'reworkFlow':#ребро к доработке документа
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'reworkFlowParallel'+str(self.parallelId))
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.outGatewayId)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), 'reworkDocument')                
                    addBPMNEdge('reworkFlowParallel'+str(self.parallelId), 
                                [(self.startX+self.gatewayAxis/2, self.currentY-self.gatewayAxis/2),
                                (self.startX*2, self.currentY-self.gatewayAxis/2),
                                (self.startX*2,self.startY+self.defaultStartEndDiametr/2+2*self.defaultFlowLength+1.5*self.defaultTaskHeight+self.gatewayAxis/2)],
                                self.xmlDiagramWriter)           
            elif qname == 'exclusiveGateway':# выходной шлюз
                for i in range(0, attrs.getLength()):
                    if attrs.getQName(i) == 'id':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), self.outGatewayId)
                    if attrs.getQName(i) == 'name':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), 'ExclusiveGateway')
                self.currentY = addBPMNShape(self.outGatewayId,
                          self.gatewayAxis,
                          self.gatewayAxis,
                          self.startX-self.gatewayAxis/2,
                          self.currentY,
                          self.xmlDiagramWriter)   
            else:
                for i in range(0, attrs.getLength()):
                    self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i))
        elif qname == 'parallelTasksDescription':#спец тег для вставки описания параллельных задач
            parallelTaskNumber = self.matchingCircuit.count()
            counter = 0
            # левый угол задачи на диаграмме
            leftCorner = self.startX - (parallelTaskNumber*self.defaultTaskWidth + (parallelTaskNumber-1)*self.gatewayAxis)/2
            for matchingCircuit in self.matchingCircuit.iterate():# цикл по параллельным залдачам
                parallelTaskParser = XMLReaderFactory.createXMLReader()
                #ответственные
                assignee, candidates, groups = extractAssigneeAndCandidates(matchingCircuit.assJSON)
                #если задач нечётное количество, то центральную задачу необходимо обрабатывать отдельно
                if parallelTaskNumber % 2 == 1 and parallelTaskNumber/2 == counter:
                    isCentral = True
                else:
                    isCentral = False
                statusVal, transitionVal = '',''
                parallelTaskHandler = parallelTaskWriter(matchingCircuit.taskKey,
                                                         matchingCircuit.name,
                                                         assignee,
                                                         candidates,
                                                         groups,
                                                         'parallelGatewayIn' + str(self.parallelGatewayIn),
                                                         'parallelGatewayOut' + str(self.parallelGatewayOut),
                                                         self.xmlWriter,
                                                         self.xmlDiagramWriter,
                                                         self.startX,
                                                         self.currentY,
                                                         leftCorner,
                                                         isCentral,
                                                         statusVal,
                                                         transitionVal)
                leftCorner = leftCorner + self.defaultTaskWidth + self.gatewayAxis
                parallelTaskParser.setContentHandler(parallelTaskHandler)
                parallelTaskParser.setErrorHandler(parallelTaskHandler)
                parallelTaskParser.setFeature("http://xml.org/sax/features/namespace-prefixes", True)
                parallelTaskParser.setProperty("http://xml.org/sax/properties/lexical-handler", parallelTaskHandler)
                stream = FileInputStream(self.parallelTaskPath)
                # парсинг шаблона параллельной задачи
                parallelTaskParser.parse(InputSource(stream))  
                counter = counter + 1
            #обновление абсциссы    
            self.currentY = self.currentY + 3.5*self.gatewayAxis + self.defaultTaskHeight
            
    def endElement(self, uri, lname, qname):
        if qname != "specialTag" and qname != 'parallelTasksDescription':
            self.xmlWriter.writeEndElement()

    def characters(self, ch, start, length):
        #для первоц задачи надо изменить условие перехода
        if self.inGatewayId == 'deleteDocumentExclusivegateway':
            rep = '${deleteDocument == "false"}'
            pattern = list('${orderApproved == "true"}')
            flag = True
            if len(pattern) == length:
                for i in range(length):
                    if ch[start+i] != pattern[i]:
                        flag = False
            else:
                flag = False
            if flag:
                self.xmlWriter.writeCharacters(rep, 0, len(rep))
            else:
                self.xmlWriter.writeCharacters(ch, start, length)
        else:
            self.xmlWriter.writeCharacters(ch, start, length)

    def comment(self, ch, start, length):
        self.xmlWriter.writeComment(''.join(ch[start:start + length]))

    def startPrefixMapping(self, prefix, uri):
        if prefix == "":
            self.xmlWriter.setDefaultNamespace(uri)
        else:
            self.xmlWriter.setPrefix(prefix, uri)

    def processingInstruction(self, target, data):
        self.xmlWriter.writeProcessingInstruction(target, data)

    def skippedEntity(self, name):
        self.xmlWriter.writeEntityRef(name)
        
class parallelTaskWriter(DefaultHandler2):
    u'''SAX-parser для одной параллельной задачи'''
    def __init__(self,taskId,taskName, assignee, candidates,groups,
                 flowIn,flowOut,xmlWriter, xmlDiagramWriter,
                  startX, currentY, leftCorner, isCentral,
                  statusVal, transitionVal):
        #идентификатор входного и выходного шлюза
        self.flowIn = flowIn
        self.flowOut = flowOut
        # идентификатор и имя задачи
        self.taskId = taskId
        self.taskName = taskName
        # ответственные
        self.assignee = assignee
        self.xmlWriter = xmlWriter
        self.candidates = candidates
        self.groups = groups
        # параметры диаграммы
        self.startX = startX
        self.currentY = currentY
        self.leftCorner = leftCorner
        self.defaultStartEndDiametr = 35
        self.gatewayAxis = 40
        self.defaultTaskHeight = 55
        self.defaultTaskWidth = 300
        self.defaultFlowLength = 75
        self.isCentral = isCentral
        self.xmlDiagramWriter = xmlDiagramWriter
        #Идентификтаор статуса задачи
        self.statusVal = statusVal
        self.transtionVal = transitionVal

    def startElement(self, namespaceURI, lname, qname, attrs):
        if qname != "specialTag":#специальные тег не пишем
            self.xmlWriter.writeStartElement(qname)
            if qname == 'userTask': # задача              
                for i in range(0, attrs.getLength()):
                    if attrs.getQName(i) == 'id':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskId)
                    elif attrs.getQName(i) == 'name':
                        self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskName)
                    elif attrs.getQName(i) == 'activiti:assignee':
                        if self.assignee != '':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.assignee)
                    elif attrs.getQName(i) == 'activiti:candidateUsers':
                        if self.candidates != '':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.candidates)
                    elif attrs.getQName(i) == 'activiti:candidateGroups':
                        if self.groups != '':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.groups)
                    else:
                        self.xmlWriter.writeAttribute(attrs.getQName(i),attrs.getValue(i))
                addBPMNShape(self.taskId,
                          self.defaultTaskHeight,
                          self.defaultTaskWidth,
                          self.leftCorner,
                          self.currentY+self.gatewayAxis*1.5,
                          self.xmlDiagramWriter)
            elif qname == 'sequenceFlow':# входное ребро
                id = attrs.getValue('id')
                if id == 'flowIn':
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.flowIn+self.taskId+'parallelTask')
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.flowIn)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskId)
                    if self.isCentral:
                        addBPMNEdge(self.flowIn+self.taskId+'parallelTask',
                                     [(self.startX, self.currentY),
                                      (self.leftCorner+self.defaultTaskWidth/2,self.currentY + 2*self.gatewayAxis)],
                                     self.xmlDiagramWriter)  
                    else:
                        addBPMNEdge(self.flowIn+self.taskId+'parallelTask',
                                     [(self.startX - self.gatewayAxis/2, self.currentY - self.gatewayAxis/2),
                                     (self.leftCorner+self.defaultTaskWidth/2, self.currentY - self.gatewayAxis/2),
                                     (self.leftCorner+self.defaultTaskWidth/2,self.currentY + 2*self.gatewayAxis)],
                                     self.xmlDiagramWriter)   
                elif id == 'flowOut':# выходное ребро
                    for i in range(0, attrs.getLength()):
                        if attrs.getQName(i) == 'id':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.flowOut+self.taskId+'parallelTask')
                        if attrs.getQName(i) == 'sourceRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.taskId)
                        if attrs.getQName(i) == 'targetRef':
                            self.xmlWriter.writeAttribute(attrs.getQName(i), self.flowOut)
                    if self.isCentral:
                        addBPMNEdge(self.flowOut+self.taskId+'parallelTask',
                                     [(self.leftCorner+self.defaultTaskWidth/2, self.currentY + 2*self.gatewayAxis + self.defaultTaskHeight),
                    
                                      (self.startX, self.currentY + self.defaultTaskHeight + 3*self.gatewayAxis)],
                                     self.xmlDiagramWriter)                           
                    else:
                        addBPMNEdge(self.flowOut+self.taskId+'parallelTask',
                                     [(self.leftCorner+self.defaultTaskWidth/2, self.currentY + 2*self.gatewayAxis + self.defaultTaskHeight),
                                      (self.leftCorner+self.defaultTaskWidth/2, self.currentY + self.defaultTaskHeight + 3.5*self.gatewayAxis),
                                      (self.startX - self.gatewayAxis/2, self.currentY + self.defaultTaskHeight + 3.5*self.gatewayAxis)],
                                     self.xmlDiagramWriter)                                            
            else:
                for i in range(0, attrs.getLength()):
                    self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i))


    def endElement(self, uri, lname, qname):
        if qname != "specialTag":
            self.xmlWriter.writeEndElement()

    def characters(self, ch, start, length):
        u'''Надо подставить значение статуса'''
        rep = "task.setVariableLocal('status', '%s')" % self.statusVal
        pattern = list('task.setVariableLocal("status", "createStatus")')
        statusFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
        rep = "task.setVariableLocal('transitions', '%s')" % self.transtionVal
        pattern = list('task.setVariableLocal("transitions", "createTransitions")')
        transitionFlag = findAndReplacePattern(ch,start,length,rep,pattern,self.xmlWriter)
        if not statusFlag and not transitionFlag:
            self.xmlWriter.writeCharacters(ch, start, length)

    def comment(self, ch, start, length):
        self.xmlWriter.writeComment(''.join(ch[start:start + length]))

    def startPrefixMapping(self, prefix, uri):
        if prefix == "":
            self.xmlWriter.setDefaultNamespace(uri)
        else:
            self.xmlWriter.setPrefix(prefix, uri)

    def processingInstruction(self, target, data):
        self.xmlWriter.writeProcessingInstruction(target, data)

    def skippedEntity(self, name):
        self.xmlWriter.writeEntityRef(name)
        
        
class diagramWriter(DefaultHandler2):
    u'''SAX-parser для описания диаграммы процесса'''
    def __init__(self,xmlWriter):
        self.xmlWriter = xmlWriter


    def startElement(self, namespaceURI, lname, qname, attrs):
        if qname != 'specialTag':# специальный тег не пишем
            self.xmlWriter.writeStartElement(qname)
            for i in range(0, attrs.getLength()):
                self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i))


    def endElement(self, uri, lname, qname):
        if qname != 'specialTag':
            self.xmlWriter.writeEndElement()

    def characters(self, ch, start, length):
        self.xmlWriter.writeCharacters(ch, start, length)

    def comment(self, ch, start, length):
        self.xmlWriter.writeComment(''.join(ch[start:start + length]))

    def startPrefixMapping(self, prefix, uri):
        if prefix == "":
            self.xmlWriter.setDefaultNamespace(uri)
        else:
            self.xmlWriter.setPrefix(prefix, uri)

    def processingInstruction(self, target, data):
        self.xmlWriter.writeProcessingInstruction(target, data)

    def skippedEntity(self, name):
        self.xmlWriter.writeEntityRef(name)