11#include "Poco/SAX/SAXException.h"
12#include <Poco/DOM/DOMParser.h>
13#include <Poco/DOM/Document.h>
14#include <Poco/DOM/NodeFilter.h>
15#include <Poco/DOM/TreeWalker.h>
16#include <Poco/Net/HTTPResponse.h>
18#include <boost/algorithm/string.hpp>
19#include <boost/regex.hpp>
24using Kernel::InternetHelper;
25using Poco::XML::Document;
26using Poco::XML::DOMParser;
27using Poco::XML::Element;
29using Poco::XML::NodeFilter;
30using Poco::XML::TreeWalker;
33static constexpr const char *URL_PREFIX =
"http://data.isis.rl.ac.uk/journals/ndx";
34static constexpr const char *INDEX_FILE_NAME =
"main";
35static constexpr const char *JOURNAL_PREFIX =
"/journal_";
36static constexpr const char *JOURNAL_EXT =
".xml";
37static constexpr const char *NXROOT_TAG =
"NXroot";
38static constexpr const char *NXENTRY_TAG =
"NXentry";
39static constexpr const char *JOURNAL_TAG =
"journal";
40static constexpr const char *FILE_TAG =
"file";
50std::string constructURL(std::string instrument, std::string
const &name) {
51 boost::algorithm::to_lower(instrument);
52 std::ostringstream url;
53 url << URL_PREFIX << instrument << JOURNAL_PREFIX << name << JOURNAL_EXT;
62Poco::AutoPtr<Document> parse(std::string
const &xmlString) {
64 Poco::AutoPtr<Document> xmldoc;
66 xmldoc = domParser.parseString(xmlString);
67 }
catch (Poco::XML::SAXParseException
const &ex) {
68 std::ostringstream msg;
69 msg <<
"ISISJournal: Error parsing file: " << ex.what();
70 throw std::runtime_error(msg.str());
79void throwIfNotValid(Element *element,
const char *expectedName) {
81 std::ostringstream msg;
82 msg <<
"ISISJournal::throwIfNotValid() - invalid element for '" << expectedName <<
"'\n";
83 throw std::invalid_argument(msg.str());
86 if (element->nodeName() != expectedName) {
87 std::ostringstream msg;
88 msg <<
"ISISJournal::throwIfNotValid() - Element name does not match '" << expectedName <<
"'. Found "
89 << element->nodeName() <<
"\n";
90 throw std::invalid_argument(msg.str());
99std::string getTextValue(Node *node) {
101 return std::string();
103 auto child = node->firstChild();
104 if (child && child->nodeName() ==
"#text") {
105 auto value = child->nodeValue();
106 boost::algorithm::trim(
value);
110 return std::string();
120 for (
auto const &filterKvp : filters) {
121 auto const childElement = element->getChildElement(filterKvp.first);
122 if (getTextValue(childElement) != filterKvp.second)
145void addValuesForElement(Element *element, std::vector<std::string>
const &valuesToLookup,
147 for (
auto &name : valuesToLookup)
148 result[name] = getTextValue(element->getChildElement(name));
161std::vector<ISISJournal::RunData> getValuesForAllElements(Element *parentElement,
162 std::vector<std::string>
const &valuesToLookup,
164 auto results = std::vector<ISISJournal::RunData>{};
166 auto nodeIter = TreeWalker(parentElement, NodeFilter::SHOW_ELEMENT);
167 for (
auto node = nodeIter.nextNode(); node; node = nodeIter.nextSibling()) {
168 auto element =
dynamic_cast<Element *
>(node);
169 throwIfNotValid(element, NXENTRY_TAG);
171 if (matchesAllFilters(element, filters)) {
172 auto result = createRunDataForElement(element);
173 addValuesForElement(element, valuesToLookup, result);
174 results.emplace_back(std::move(result));
187std::vector<std::string> getAttributeForAllChildElements(Element *parentElement,
const char *childElementName,
188 const char *attributeName) {
189 auto results = std::vector<std::string>{};
191 auto nodeIter = TreeWalker(parentElement, NodeFilter::SHOW_ELEMENT);
192 for (
auto node = nodeIter.nextNode(); node; node = nodeIter.nextSibling()) {
193 auto element =
dynamic_cast<Element *
>(node);
194 throwIfNotValid(element, childElementName);
195 results.emplace_back(element->getAttribute(attributeName));
206std::string convertFilenameToCycleName(std::string
const &filename) {
207 boost::regex pattern(
"[0-9]+_[0-9]+");
208 boost::smatch matches;
209 boost::regex_search(filename, matches, pattern);
211 if (matches.size() == 1)
214 return std::string();
223std::vector<std::string> convertFilenamesToCycleNames(std::vector<std::string>
const &filenames) {
224 auto cycles = std::vector<std::string>();
225 cycles.reserve(filenames.size());
226 for (
const auto &filename : filenames) {
227 auto cycle = convertFilenameToCycleName(filename);
229 cycles.emplace_back(std::move(cycle));
242 std::unique_ptr<InternetHelper> internetHelper)
243 : m_internetHelper(
std::move(internetHelper)), m_runsFileURL(constructURL(instrument, cycle)),
244 m_indexFileURL(constructURL(instrument, INDEX_FILE_NAME)) {}
263 throwIfNotValid(rootElement, JOURNAL_TAG);
264 auto filenames = getAttributeForAllChildElements(rootElement, FILE_TAG,
"name");
265 return convertFilenamesToCycleNames(filenames);
283 throwIfNotValid(rootElement, NXROOT_TAG);
284 return getValuesForAllElements(rootElement, valuesToLookup, filters);
293 std::ostringstream serverReply;
298 std::ostringstream msg;
299 msg <<
"Failed to access file " << url <<
"\nCheck that the cycle name is valid.";
304 std::ostringstream msg;
305 msg <<
"Failed to access file " << url <<
"\nHTTP Code: " <<
static_cast<int>(statusCode)
306 <<
"\nCheck that the cycle name is valid.";
309 return serverReply.str();
const std::vector< double > & rhs
double value
The value of the point.
std::map< std::string, std::string > RunData
ISISJournal: Helper class to aid in fetching ISIS specific run information from journal files.
std::string getURLContents(std::string const &url)
Get the contents of a file at a given URL.
std::vector< std::string > getCycleNames() override
Get the list of cycle names.
std::unique_ptr< Kernel::InternetHelper > m_internetHelper
std::string m_runsFileURL
Poco::AutoPtr< Poco::XML::Document > m_indexDocument
ISISJournal const & operator=(ISISJournal const &rhs)=delete
ISISJournal(std::string const &instrument, std::string const &cycle, std::unique_ptr< Kernel::InternetHelper > internetHelper=std::make_unique< Kernel::InternetHelper >())
Construct the journal class for a specific instrument and cycle.
Poco::AutoPtr< Poco::XML::Document > m_runsDocument
std::string m_indexFileURL
std::vector< RunData > getRuns(std::vector< std::string > const &valuesToLookup={}, RunData const &filters=RunData()) override
Get data for runs that match the given filters.
Exception thrown when error occurs accessing an internet resource.