ru.curs.xylophone.SAXDataReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xylophone Show documentation
Show all versions of xylophone Show documentation
xylophone - LGPL Java library
/*
(с) 2016 ООО "КУРС-ИТ"
Этот файл — часть КУРС:Xylophone.
КУРС:Xylophone — свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Стандартной общественной лицензии ограниченного применения GNU (LGPL)
в том виде, в каком она была опубликована Фондом свободного программного обеспечения; либо
версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
общественной лицензии GNU.
Вы должны были получить копию Стандартной общественной лицензии ограниченного
применения GNU (LGPL) вместе с этой программой. Если это не так,
см. http://www.gnu.org/licenses/.
Copyright 2016, COURSE-IT Ltd.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
*/
package ru.curs.xylophone;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import ru.curs.xylophone.XMLContext.SAXContext;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.InputStream;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**
* Класс, ответственный за чтение из XML-файла и перенаправление команд на вывод
* в объект ReportWriter методом SAX.
*/
final class SAXDataReader extends XMLDataReader {
private final Source xmlData;
SAXDataReader(InputStream xmlData, DescriptorElement xmlDescriptor,
ReportWriter writer) {
super(writer, xmlDescriptor);
this.xmlData = new StreamSource(xmlData);
}
/**
* Адаптирует дескриптор элемента к SAX-парсингу.
*/
private static final class SAXElementDescriptor {
private int elementIndex = -1;
private int position = 0;
private final int desiredIndex;
private final XMLContext context;
private final boolean iterate;
private final boolean horizontal;
private final List preOutputs = new LinkedList<>();
private final List expectedElements = new LinkedList<>();
private final List postOutputs = new LinkedList<>();
private final List headerOutputs = new LinkedList<>();
private final List footerOutputs = new LinkedList<>();
private final int merge;
private final String regionName;
SAXElementDescriptor() {
context = null;
iterate = false;
horizontal = false;
desiredIndex = -1;
merge = 0;
regionName = null;
}
SAXElementDescriptor(DescriptorElement e, XMLContext context)
throws SAXException {
this.context = context;
boolean iterate = false;
boolean horizontal = false;
int desiredIndex = -1;
int merge = 0;
String regionName = null;
for (DescriptorSubelement se : e.getSubelements())
if (!iterate) {
// До тэга iteration
if (se instanceof DescriptorOutput)
preOutputs.add((DescriptorOutput) se);
else if (se instanceof DescriptorIteration) {
for (DescriptorElement de : ((DescriptorIteration) se)
.getElements()) {
if ("(before)".equals(de.getElementName())) {
for (DescriptorSubelement se2 : de
.getSubelements())
if (se2 instanceof DescriptorOutput)
headerOutputs
.add((DescriptorOutput) se2);
} else if ("(after)".equals(de.getElementName())) {
for (DescriptorSubelement se2 : de
.getSubelements())
if (se2 instanceof DescriptorOutput)
footerOutputs
.add((DescriptorOutput) se2);
} else
expectedElements.add(de);
}
desiredIndex = ((DescriptorIteration) se).getIndex();
iterate = true;
horizontal = ((DescriptorIteration) se).isHorizontal();
merge = ((DescriptorIteration) se).getMerge();
regionName = ((DescriptorIteration) se).getRegionName();
}
} else {
// После тэга iteration
if (se instanceof DescriptorOutput)
postOutputs.add((DescriptorOutput) se);
else if (se instanceof DescriptorIteration)
throw new SAXException(
"For SAX mode only one iteration element is allowed for each element descriptor.");
}
this.iterate = iterate;
this.horizontal = horizontal;
this.desiredIndex = desiredIndex;
this.merge = merge;
this.regionName = regionName;
}
}
@Override
void process() throws XML2SpreadSheetError {
final class Parser extends DefaultHandler {
private final Deque elementsStack = new LinkedList<>();
private void bypass() {
elementsStack.push(new SAXElementDescriptor());
}
@Override
public void startElement(String uri, String localName, String name,
Attributes atts) throws SAXException {
SAXElementDescriptor curDescr = elementsStack.peek();
curDescr.elementIndex++;
if (compareIndices(curDescr.desiredIndex, curDescr.elementIndex)) {
boolean found = false;
HashMap attsmap = new HashMap<>();
for (int i = 0; i < atts.getLength(); i++)
attsmap.put(atts.getLocalName(i), atts.getValue(i));
searchElements:
for (DescriptorElement e : curDescr.expectedElements) {
if (compareNames(e.getElementName(), localName, attsmap)) {
XMLContext context = new SAXContext(atts,
curDescr.position + 1);
SAXElementDescriptor sed = new SAXElementDescriptor(
e, context);
elementsStack.push(sed);
// По пред-выводам выполняем вывод.
for (DescriptorOutput o : sed.preOutputs)
try {
processOutput(sed.context, o);
} catch (XML2SpreadSheetError e1) {
throw new SAXException(e1.getMessage());
}
// Начинаем обрамление итерации
try {
if (sed.iterate) {
getWriter().startSequence(sed.horizontal);
for (DescriptorOutput deo : sed.headerOutputs)
processOutput(sed.context, deo);
}
} catch (XML2SpreadSheetError e1) {
throw new SAXException(e1.getMessage());
}
found = true;
break searchElements;
}
}
if (found)
curDescr.position++;
else
bypass();
} else
bypass();
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
SAXElementDescriptor sed = elementsStack.pop();
try {
// Завершаем обрамление итерации
if (sed.iterate) {
for (DescriptorOutput deo : sed.footerOutputs)
processOutput(sed.context, deo);
getWriter().endSequence(sed.merge, sed.regionName);
}
// По пост-выводам выполняем вывод
for (DescriptorOutput o : sed.postOutputs)
processOutput(sed.context, o);
} catch (XML2SpreadSheetError e1) {
throw new SAXException(e1.getMessage());
}
}
}
Parser parser = new Parser();
SAXElementDescriptor sed = new SAXElementDescriptor();
sed.expectedElements.add(getDescriptor());
parser.elementsStack.push(sed);
try {
TransformerFactory.newInstance().newTransformer()
.transform(xmlData, new SAXResult(parser));
} catch (Exception e) {
throw (XML2SpreadSheetError)
new XML2SpreadSheetError("Error while processing XML data: "
+ e.getMessage()).initCause(e);
}
getWriter().flush();
}
}