Coverage Summary for Class: XmlResponseHandler (org.kitodo.queryurlimport)
Class |
Class, %
|
Method, %
|
Line, %
|
XmlResponseHandler |
100%
(1/1)
|
66,7%
(6/9)
|
35,2%
(19/54)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.queryurlimport;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.Objects;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.kitodo.api.externaldatamanagement.DataImport;
import org.kitodo.api.externaldatamanagement.SearchInterfaceType;
import org.kitodo.api.externaldatamanagement.SearchResult;
import org.kitodo.api.externaldatamanagement.SingleHit;
import org.kitodo.exceptions.CatalogException;
import org.kitodo.exceptions.ConfigException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
class XmlResponseHandler {
private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private static final XMLOutputter xmlOutputter = new XMLOutputter();
private static final XPath xPath = XPathFactory.newInstance().newXPath();
static {
documentBuilderFactory.setNamespaceAware(true);
try {
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (ParserConfigurationException parserConfigurationException) {
throw new UndeclaredThrowableException(parserConfigurationException);
}
xmlOutputter.setFormat(Format.getPrettyFormat());
}
/**
* Create and return SearchResult for given HttpResponse.
* @param response HttpResponse for which a SearchResult is created
* @return SearchResult created from given HttpResponse
*/
static SearchResult getSearchResult(HttpResponse response, DataImport dataImport) throws IOException {
SearchInterfaceType interfaceType = dataImport.getSearchInterfaceType();
SearchResult searchResult = new SearchResult();
if (Objects.nonNull(response) && Objects.nonNull(response.getEntity())) {
String content = IOUtils.toString(response.getEntity().getContent(), Charset.defaultCharset());
Document resultDocument = transformResponseToDocument(content);
if (Objects.nonNull(resultDocument)) {
searchResult.setHits(extractHits(resultDocument, dataImport));
if (Objects.nonNull(interfaceType.getNumberOfRecordsString())) {
searchResult.setNumberOfHits(extractNumberOfRecords(resultDocument, interfaceType));
} else {
searchResult.setNumberOfHits(searchResult.getHits().size());
}
if (searchResult.getNumberOfHits() < 1 && Objects.nonNull(interfaceType.getErrorMessageXpath())) {
String errorMessage = getTextContent(resultDocument.getDocumentElement(),
interfaceType.getErrorMessageXpath());
if (StringUtils.isNotBlank(errorMessage)) {
errorMessage = interfaceType.getTypeString().toUpperCase() + " error: '" + errorMessage + "'";
throw new CatalogException(errorMessage);
}
}
}
}
return searchResult;
}
/**
* Transform given HttpResponse into Document and return it.
* @param content String that is transformed into a Document
* @return Document into which given HttpResponse has been transformed
*/
private static Document transformResponseToDocument(String content) {
if (Objects.nonNull(content)) {
return parseXML(content);
}
throw new ConfigException("Query response is null");
}
private static Document parseXML(String xmlString) {
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
return documentBuilder.parse(new InputSource(new ByteArrayInputStream(xmlString
.getBytes(StandardCharsets.UTF_8))));
} catch (ParserConfigurationException | IOException | SAXException e) {
throw new ConfigException(e.getMessage());
}
}
private static LinkedList<SingleHit> extractHits(Document document, DataImport dataImport) {
SearchInterfaceType type = dataImport.getSearchInterfaceType();
LinkedList<SingleHit> hits = new LinkedList<>();
NodeList records = document.getElementsByTagNameNS(type.getNamespace(), type.getRecordString());
for (int i = 0; i < records.getLength(); i++) {
Element recordElement = (Element) records.item(i);
String recordTitle = getTextContent(recordElement, dataImport.getRecordTitleXPath());
String recordId = getTextContent(recordElement, dataImport.getRecordIdXPath());
hits.add(new SingleHit(recordTitle, recordId));
}
return hits;
}
static int extractNumberOfRecords(String content, SearchInterfaceType type) {
return extractNumberOfRecords(transformResponseToDocument(content), type);
}
private static int extractNumberOfRecords(Document document, SearchInterfaceType type) {
NodeList numberOfRecordNodes = document.getElementsByTagNameNS(type.getNamespace(), type.getNumberOfRecordsString());
assert numberOfRecordNodes.getLength() == 1;
Element numberOfRecordsElement = (Element) numberOfRecordNodes.item(0);
if (Objects.nonNull(numberOfRecordsElement)) {
return Integer.parseInt(numberOfRecordsElement.getTextContent().trim());
}
return 0;
}
static String getTextContent(Element element, String xpathString) {
try {
return xPath.evaluate(xpathString, element);
} catch (XPathExpressionException e) {
return "";
}
}
}