Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "MantidKernel/MaterialXMLParser.h"
#include "MantidKernel/MaterialBuilder.h"
#include "Poco/DOM/AutoPtr.h"
#include "Poco/DOM/DOMParser.h"
#include "Poco/DOM/Document.h"
#include "Poco/DOM/NamedNodeMap.h"
#include "Poco/DOM/NodeIterator.h"
#include "Poco/DOM/NodeFilter.h"
#include "Poco/SAX/InputSource.h"
#include "Poco/SAX/SAXException.h"
#include <boost/lexical_cast.hpp>
#include <sstream>
#include <type_traits>
#include <unordered_map>
namespace Mantid {
namespace Kernel {
// -----------------------------------------------------------------------------
// Anonymous methods
// -----------------------------------------------------------------------------
namespace {
/*
* Define the list of known attributes along with a map to the relevant
* MaterialBuilder method to call. This avoids a long list of if/else
* statements when parsing the xml.
*
* The mapping uses a template type, TypedBuilderHandle, to store the method
* on the MaterialBuilder that should be called and the template type indicates
* the argument type of the method call. The struct automatically extracts
* the expected value type from the given string based on the template type.
*/
// Known attributes
const char *ID_ATT = "id";
const char *FORMULA_ATT = "formula";
const char *ATOMNUM_ATT = "atomicnumber";
const char *MASSNUM_ATT = "massnumber";
const char *NDENSITY_ATT = "numberdensity";
const char *ZPARAM_ATT = "zparameter";
const char *CELLVOL_ATT = "unitcellvol";
const char *MASSDENS_ATT = "massdensity";
const char *TOTSC_ATT = "totalscatterxsec";
const char *COHSC_ATT = "cohscatterxsec";
const char *INCOHSC_ATT = "incohscatterxsec";
const char *ABSORB_ATT = "absorptionxsec";
// Base type to put in a hash
struct BuilderHandle {
virtual ~BuilderHandle() = default;
virtual void operator()(MaterialBuilder &builder,
const std::string &value) const = 0;
};
using BuilderHandle_uptr = std::unique_ptr<BuilderHandle>;
// Pointer to member function on MaterialBuilder
template <typename ArgType>
using BuilderMethod = MaterialBuilder &(MaterialBuilder::*)(ArgType);
// Template type where ArgType indicates the expected argument type including
// const & ref qualifiers
template <typename ArgType>
struct TypedBuilderHandle final : public BuilderHandle {
// Remove const/reference qualifiers from ArgType
using ValueType = typename std::remove_const<
typename std::remove_reference<ArgType>::type>::type;
explicit TypedBuilderHandle(BuilderMethod<ArgType> m)
: BuilderHandle(), m_method(m) {}
void operator()(MaterialBuilder &builder,
const std::string &value) const override {
auto typedVal = boost::lexical_cast<ValueType>(value);
(builder.*m_method)(typedVal);
}
private:
BuilderMethod<ArgType> m_method;
};
using Handlers = std::unordered_map<std::string, BuilderHandle_uptr>;
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Insert a handle into the given map
template <typename ArgType>
void insertHandle(Handlers *hash, const std::string &name,
BuilderMethod<ArgType> m) {
hash->insert(std::make_pair(
name, BuilderHandle_uptr(new TypedBuilderHandle<ArgType>(m))));
}
// Find the appropriate handler for a given attribute
const BuilderHandle &findHandle(const std::string &name) {
static Handlers handles;
if (handles.empty()) {
insertHandle(&handles, ID_ATT, &MaterialBuilder::setName);
insertHandle(&handles, FORMULA_ATT, &MaterialBuilder::setFormula);
insertHandle(&handles, ATOMNUM_ATT, &MaterialBuilder::setAtomicNumber);
insertHandle(&handles, MASSNUM_ATT, &MaterialBuilder::setMassNumber);
insertHandle(&handles, NDENSITY_ATT, &MaterialBuilder::setNumberDensity);
insertHandle(&handles, ZPARAM_ATT, &MaterialBuilder::setZParameter);
insertHandle(&handles, CELLVOL_ATT, &MaterialBuilder::setUnitCellVolume);
insertHandle(&handles, MASSDENS_ATT, &MaterialBuilder::setMassDensity);
insertHandle(&handles, TOTSC_ATT,
&MaterialBuilder::setTotalScatterXSection);
insertHandle(&handles, COHSC_ATT, &MaterialBuilder::setCoherentXSection);
insertHandle(&handles, INCOHSC_ATT,
&MaterialBuilder::setIncoherentXSection);
insertHandle(&handles, ABSORB_ATT, &MaterialBuilder::setAbsorptionXSection);
}
auto iter = handles.find(name);
if (iter != handles.end())
return *(iter->second);
else
throw std::runtime_error("Unknown material attribute '" + name + "'");
}
/**
* Set a value on the builder base on the attribute name and the defined
* member function
* @param builder A pointer to the builder to update
* @param attr The attribute name
* @param value The value in the attribute
*/
void addToBuilder(MaterialBuilder *builder, const std::string &attr,
const std::string &value) {
// Find the appropriate member function on the builder and set the value
// We need 3 maps for the 3 allowable value types for the builder member
// functions
const auto &setter = findHandle(attr);
setter(*builder, value);
}
}
// -----------------------------------------------------------------------------
// Public methods
// -----------------------------------------------------------------------------
/**
* Takes a stream that is assumed to contain a single complete material
* definition,
* reads the definition and produces a new Material object. If many definitions
* are present then the first one is read
* @param istr A reference to a stream
* @return A new Material object
*/
Material MaterialXMLParser::parse(std::istream &istr) const {
using namespace Poco::XML;
using DocumentPtr = AutoPtr<Document>;
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
InputSource src(istr);
DOMParser parser;
// Do not use auto here or anywhereas the Poco API returns raw pointers
// but in some circumstances requires AutoPtrs to manage the memory
DocumentPtr doc;
try {
doc = parser.parse(&src);
} catch (SAXParseException &exc) {
std::ostringstream os;
os << "MaterialXMLReader::read() - Error parsing stream as XML: "
<< exc.what();
throw std::invalid_argument(os.str());
}
Element *rootElement = doc->documentElement();
// Iterating is apparently much faster than getElementsByTagName
NodeIterator nodeIter(rootElement, NodeFilter::SHOW_ELEMENT);
Node *node = nodeIter.nextNode();
Material matr;
bool found(false);
while (node) {
if (node->nodeName() == MATERIAL_TAG) {
matr = parse(static_cast<Element *>(node));
found = true;
break;
}
node = nodeIter.nextNode();
}
if (!found) {
throw std::invalid_argument(
"MaterialXMLReader::read() - No material tags found.");
}
return matr;
}
/**
* Takes a pointer to an XML node that is assumed to point at a "material"
* tag.
* It reads the definition and produces a new Material object.
* @param element A pointer to an Element node that is a "material" tag
* @return A new Material object
*/
Material MaterialXMLParser::parse(Poco::XML::Element *element) const {
using namespace Poco::XML;
using NamedNodeMapPtr = AutoPtr<NamedNodeMap>;
NamedNodeMapPtr attrs = element->attributes();
const auto id = attrs->getNamedItem(ID_ATT);
if (!id || id->nodeValue().empty()) {
throw std::invalid_argument("MaterialXMLReader::read() - No 'id' tag found "
"or emptry string provided.");
}
attrs->removeNamedItem(ID_ATT);
MaterialBuilder builder;
builder.setName(id->nodeValue());
const auto nattrs = attrs->length();
for (unsigned long i = 0; i < nattrs; ++i) {
Node *node = attrs->item(i);
addToBuilder(&builder, node->nodeName(), node->nodeValue());
}
return builder.build();
}
} // namespace Kernel
} // namespace Mantid