# coding: utf-8 import array import base64 from java.io import StringWriter from java.lang import String from java.text import SimpleDateFormat from javax.xml.stream import XMLOutputFactory from org.xml.sax.helpers import XMLReaderFactory from org.xml.sax.ext import DefaultHandler2 from org.xml.sax import InputSource import os from java.io import File, FileInputStream, FileOutputStream try: from ru.curs.showcase.core.jython import JythonDownloadResult except: from ru.curs.celesta.showcase import JythonDownloadResult from ru.curs.celesta import CelestaException class UploadXMLHandler(DefaultHandler2): """Класс SAX-парсера, производящий разбор xml и вставку данных в таблицу""" parentTag = None currentCell = None flag = 0 currentString = u'' currentEncoding = u"utf8" def __init__(self, tableInstance, action): self.tableInstance = tableInstance # возможность настраивать, нужно ли обновлять записи и вставлять новые def actionUI(ins): if not ins.tryInsert(): ins.update() def actionU(ins): ins.tryUpdate() def actionI(ins): ins.tryInsert() # определяем, какую функцию нам нужно использовать self.funcAction = locals()['action%s' % action.upper()] def startElement(self, namespaceURI, lname, qname, attrs): if self.parentTag == 'field': if self.flag == 0: self.stringWriter = StringWriter() self.xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(self.stringWriter, "UTF-8") self.flag += 1 self.xmlWriter.writeStartElement(qname) for i in range(0, attrs.getLength()): self.xmlWriter.writeAttribute(attrs.getQName(i), attrs.getValue(i)) elif qname == 'table' and self.flag == 0: if self.parentTag is not None: raise CelestaException(u"Неверный формат файла") self.parentTag = qname if not attrs.getValue('name'): raise CelestaException(u"Атрибут 'name' отсутствует в теге 'table'") elif attrs.getValue('name') != self.tableInstance.meta().getName(): raise CelestaException(u"Имя таблицы %s не соответствует значению атрибута 'name'" % self.tableInstance.meta().getName()) elif qname == 'row' and self.flag == 0: if self.parentTag != 'table': raise CelestaException(u"Неверный формат файла") self.parentTag = qname elif qname == 'field' and self.flag == 0: if self.parentTag != 'row': raise CelestaException(u"Неверный формат файла") self.currentEncoding = attrs.getValue('encoding') or u"utf8" self.currentCell = attrs.getValue('name') self.parentTag = qname self.currentString = u'' else: raise CelestaException(u"Неверный формат файла") def endElement(self, uri, lname, qname): if qname == 'table' and self.flag == 0: self.parentTag = None elif qname == 'row' and self.flag == 0: # обновляем или вставляем записи self.funcAction(self.tableInstance) self.tableInstance.clear() self.parentTag = 'table' elif qname == 'field' and self.flag == 0: # Вставка данных в поле таблицы, отдельно рассмотрен случай если данные в формате XML if hasattr(self, 'stringWriter') and self.stringWriter: self.xmlWriter.close() # Вставка данных в поле типа blob if self.tableInstance.meta().columns[self.currentCell].getCelestaType() == 'BLOB': getattr(self.tableInstance, "calc%s" % self.currentCell)() blobField = self.tableInstance.__dict__[self.currentCell].getOutStream() blobField.write(self.stringWriter.strip()) else: self.tableInstance.__setattr__(self.currentCell, self.stringWriter.toString()) self.stringWriter.flush() self.stringWriter = None self.xmlWriter = None else: # проверка на None if self.currentString.strip() != 'None': self.currentString = self.currentString.strip() # Вставка данных в поле типа blob if self.tableInstance.meta().columns[self.currentCell].getCelestaType() == 'BLOB': getattr(self.tableInstance, "calc%s" % self.currentCell)() blobField = self.tableInstance.__dict__[self.currentCell].getOutStream() if self.currentEncoding == u"utf8": blobField.write(self.currentString) elif self.currentEncoding == u"base64": blobField.write(base64.b64decode(self.currentString)) else: raise CelestaException(u"Неверная кодировка") elif self.tableInstance.meta().columns[self.currentCell].getCelestaType() == 'DATETIME': sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") self.tableInstance.__setattr__(self.currentCell, sdf.parse(self.currentString)) elif self.tableInstance.meta().columns[self.currentCell].getCelestaType() == 'BIT': self.tableInstance.__setattr__(self.currentCell, self.currentString.lower() == "true") else: if self.currentCell == 'prefix': pass self.tableInstance.__setattr__(self.currentCell, self.currentString) self.parentTag = 'row' self.currentCell = None self.currentString = None elif self.parentTag == 'field': self.xmlWriter.writeEndElement() self.flag -= 1 def characters(self, ch, start, length): if self.parentTag == 'field' and self.flag > 0: self.xmlWriter.writeCharacters(ch, start, length) elif self.currentCell: self.currentString += unicode(String(ch[start:start + length])) def comment(self, ch, start, length): if self.parentTag == 'field' and self.flag > 0: self.xmlWriter.writeComment(ch.tostring()[start:start + length]) def startPrefixMapping(self, prefix, uri): if self.parentTag == 'field' and self.flag > 0: if prefix == "": self.xmlWriter.setDefaultNamespace(uri) else: self.xmlWriter.setPrefix(prefix, uri) def processingInstruction(self, target, data): if self.parentTag == 'field' and self.flag > 0: self.xmlWriter.writeProcessingInstruction(target, data); def skippedEntity(self, name): if self.parentTag == 'field' and self.flag > 0: self.xmlWriter.writeEntityRef(name); ''' Класс реализует обмен данными между таблицами баз данных и xml файлами. Блоб данные хранятся в xml файле в формате base64 (для передачи данных из xml в БД возможен вариант хранения в виде просто текста в utf8) dataStream - входящий или исходящий поток с xml данными tableInstance - Экземпляр курсора таблицы ''' class DataBaseXMLExchange(): def __init__(self, dataStream, tableInstance): self.dataStream = dataStream self.tableInstance = tableInstance def uploadXML(self, action="ui"): ''' функция реализует загрузку данных из xml в базу данных ''' parser = XMLReaderFactory.createXMLReader(); handler = UploadXMLHandler(self.tableInstance, action) 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) parser.parse(InputSource(self.dataStream)) def blobHandler(self, field, xmlWriter): ''' функция реализует запись данных закодированных в base64 вo writer ''' getattr(self.tableInstance, u"calc%s" % field)() blobField = self.tableInstance.__dict__[unicode(field)].getInStream() if blobField is not None: byteArray = [-1, -1, -1] counter = 0 while True: byteArray[0] = blobField.read() byteArray[1] = blobField.read() byteArray[2] = blobField.read() if byteArray[0] == -1: break elif byteArray[1] == -1: xmlWriter.writeCharacters(base64.b64encode(array.array('B', byteArray[0:1]).tostring())) break elif byteArray[2] == -1: xmlWriter.writeCharacters(base64.b64encode(array.array('B', byteArray[0:2]).tostring())) break else: counter += 1 xmlWriter.writeCharacters(base64.b64encode(array.array('B', byteArray).tostring())) if counter % 20 == 0: xmlWriter.writeCharacters("\n\t\t\t") def downloadXML(self): ''' функция реализует выгрузку данных из базы данных в файл xml ''' xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(self.dataStream, "UTF-8") xmlWriter.writeStartDocument("UTF-8", "1.0") xmlWriter.writeCharacters("\n") xmlWriter.writeStartElement("table") xmlWriter.writeAttribute('name', self.tableInstance.meta().getName()) xmlWriter.writeCharacters("\n") while self.tableInstance.nextInSet(): xmlWriter.writeCharacters("\t") xmlWriter.writeStartElement("row") xmlWriter.writeCharacters("\n") for field in self.tableInstance.meta().getColumns(): xmlWriter.writeCharacters("\t\t") xmlWriter.writeStartElement("field") xmlWriter.writeAttribute('name', field) if self.tableInstance.meta().columns[field].getCelestaType() == 'BLOB': xmlWriter.writeAttribute('encoding', 'base64') self.blobHandler(field, xmlWriter) else: xmlWriter.writeCharacters("\n\t\t\t") xmlWriter.writeCharacters(unicode(self.tableInstance.__getattribute__(field))) xmlWriter.writeCharacters("\n") xmlWriter.writeCharacters("\t\t") xmlWriter.writeEndElement() xmlWriter.writeCharacters("\n") xmlWriter.writeCharacters("\t") xmlWriter.writeEndElement() xmlWriter.writeCharacters("\n") xmlWriter.writeEndElement() xmlWriter.writeEndDocument() xmlWriter.flush() def tableDownload(cursorInstance, fileName): filePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', fileName + '.xml') dataStream = FileOutputStream(filePath) exchange = DataBaseXMLExchange(dataStream, cursorInstance) exchange.downloadXML() dataStream.close() report = File(filePath) return JythonDownloadResult(FileInputStream(report), fileName + '.xml')