1 /*****************************************************************************/
2 /* XDMF */
3 /* eXtensible Data Model and Format */
4 /* */
5 /* Id : XdmfCoreReader.cpp */
6 /* */
7 /* Author: */
8 /* Kenneth Leiter */
9 /* kenneth.leiter@arl.army.mil */
10 /* US Army Research Laboratory */
11 /* Aberdeen Proving Ground, MD */
12 /* */
13 /* Copyright @ 2011 US Army Research Laboratory */
14 /* All Rights Reserved */
15 /* See Copyright.txt for details */
16 /* */
17 /* This software is distributed WITHOUT ANY WARRANTY; without */
18 /* even the implied warranty of MERCHANTABILITY or FITNESS */
19 /* FOR A PARTICULAR PURPOSE. See the above copyright notice */
20 /* for more information. */
21 /* */
22 /*****************************************************************************/
23
24 #include <boost/algorithm/string/trim.hpp>
25 #include <boost/tokenizer.hpp>
26 #include <cstring>
27 #include <map>
28 #include <sstream>
29 #include <utility>
30 #include "XdmfArray.hpp"
31 #include "XdmfArrayType.hpp"
32 #include "XdmfCoreItemFactory.hpp"
33 #include "XdmfCoreReader.hpp"
34 #include "XdmfError.hpp"
35 #include "XdmfFunction.hpp"
36 #include "XdmfSubset.hpp"
37 #include "XdmfItem.hpp"
38 #include "XdmfSystemUtils.hpp"
39
40 /**
41 * PIMPL
42 */
43 class XdmfCoreReader::XdmfCoreReaderImpl {
44
45 public:
46
XdmfCoreReaderImpl(const shared_ptr<const XdmfCoreItemFactory> itemFactory,const XdmfCoreReader * const coreReader)47 XdmfCoreReaderImpl(const shared_ptr<const XdmfCoreItemFactory> itemFactory,
48 const XdmfCoreReader * const coreReader) :
49 mCoreReader(coreReader),
50 mItemFactory(itemFactory)
51 {
52 };
53
~XdmfCoreReaderImpl()54 ~XdmfCoreReaderImpl()
55 {
56 };
57
58 void
closeFile()59 closeFile()
60 {
61 mXPathMap.clear();
62 xmlXPathFreeContext(mXPathContext);
63 for(std::map<std::string, xmlDocPtr>::const_iterator iter =
64 mDocuments.begin(); iter != mDocuments.end(); ++iter) {
65 xmlFreeDoc(iter->second);
66 }
67 mDocuments.clear();
68
69 xmlCleanupParser();
70 }
71
72 void
openFile(const std::string & filePath)73 openFile(const std::string & filePath)
74 {
75 mXMLDir = XdmfSystemUtils::getRealPath(filePath);
76 size_t index = mXMLDir.find_last_of("/\\");
77 if(index != std::string::npos) {
78 mXMLDir = mXMLDir.substr(0, index + 1);
79 }
80
81 mDocument = xmlReadFile(filePath.c_str(), NULL, XML_PARSE_NOENT);
82
83 if(mDocument == NULL) {
84 XdmfError::message(XdmfError::FATAL,
85 "xmlReadFile could not read " + filePath +
86 " in XdmfCoreReader::XdmfCoreReaderImpl::openFile");
87 }
88
89 mDocuments.insert(std::make_pair((char*)mDocument->URL, mDocument));
90
91 mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
92 mXPathMap.clear();
93 }
94
95 void
parse(const std::string & lightData)96 parse(const std::string & lightData)
97 {
98 mDocument = xmlParseDoc((const xmlChar*)lightData.c_str());
99
100 if(mDocument == NULL) {
101 XdmfError::message(XdmfError::FATAL,
102 "xmlReadFile could not parse passed light data string"
103 " in XdmfCoreReader::XdmfCoreReaderImpl::parse");
104 }
105
106 //mDocuments.insert(std::make_pair((char*)mDocument->URL, mDocument));
107 mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
108 mXPathMap.clear();
109 }
110
111 /**
112 * Constructs XdmfItems for all nodes in currNode's tree.
113 * XdmfItems are constructed by recursively calling this function for all
114 * children of currNode.
115 */
116 std::vector<shared_ptr<XdmfItem> >
read(xmlNodePtr currNode)117 read(xmlNodePtr currNode)
118 {
119 std::vector<shared_ptr<XdmfItem> > myItems;
120
121 while(currNode != NULL) {
122 if(currNode->type == XML_ELEMENT_NODE) {
123 // Normal reading
124 this->readSingleNode(currNode, myItems);
125 }
126 currNode = currNode->next;
127 }
128 return myItems;
129 }
130
131 /**
132 * Reads a single xmlNode into an XdmfItem object in memory. The constructed
133 * XdmfItem is added to myItems and an entry is added mapping the xmlNodePtr
134 * to the new XdmfItem in the mXPathMap.
135 */
136 void
readSingleNode(const xmlNodePtr currNode,std::vector<shared_ptr<XdmfItem>> & myItems)137 readSingleNode(const xmlNodePtr currNode,
138 std::vector<shared_ptr<XdmfItem> > & myItems)
139 {
140 // Deal with proper resolution of XIncludes
141 if(xmlStrcmp(currNode->name, (xmlChar*)"include") == 0) {
142
143 xmlChar * xpointer = NULL;
144 xmlChar * href = NULL;
145
146 xmlAttrPtr currAttribute = currNode->properties;
147 while(currAttribute != NULL) {
148 if(xmlStrcmp(currAttribute->name, (xmlChar*)"xpointer") == 0) {
149 xpointer = currAttribute->children->content;
150 }
151 if(xmlStrcmp(currAttribute->name, (xmlChar*)"href") == 0) {
152 href = currAttribute->children->content;
153 }
154 currAttribute = currAttribute->next;
155 }
156
157 xmlXPathContextPtr oldContext = mXPathContext;
158 if(href) {
159 xmlDocPtr document;
160 xmlChar * filePath = xmlBuildURI(href, mDocument->URL);
161 std::map<std::string, xmlDocPtr>::const_iterator iter =
162 mDocuments.find((char*)filePath);
163 if(iter == mDocuments.end()) {
164 document = xmlReadFile((char*)filePath, NULL, 0);
165 mDocuments.insert(std::make_pair((char*)document->URL,
166 document));
167 }
168 else {
169 document = iter->second;
170 }
171
172 mXPathContext = xmlXPtrNewContext(document, NULL, NULL);
173 }
174
175 if(xpointer) {
176 xmlXPathObjectPtr result = xmlXPtrEval(xpointer, mXPathContext);
177 if(result && !xmlXPathNodeSetIsEmpty(result->nodesetval)) {
178 for(int i=0; i<result->nodesetval->nodeNr; ++i) {
179 this->readSingleNode(result->nodesetval->nodeTab[i],
180 myItems);
181 }
182 }
183 else {
184 XdmfError::message(XdmfError::FATAL,
185 "Invalid xpointer encountered.");
186 }
187 xmlXPathFreeObject(result);
188 }
189
190 if(href) {
191 xmlXPathFreeContext(mXPathContext);
192 }
193
194 mXPathContext = oldContext;
195
196 }
197 else {
198
199 // Check to see if the node is already in the XPath Map (seen previously)
200 std::map<xmlNodePtr, shared_ptr<XdmfItem> >::const_iterator iter =
201 mXPathMap.find(currNode);
202 // If it is grab it from the previously stored items
203 if(iter != mXPathMap.end()) {
204 myItems.push_back(iter->second);
205 }
206 else {
207 // Otherwise, generate a new XdmfItem from the node
208 std::map<std::string, std::string> itemProperties;
209
210 xmlNodePtr childNode = currNode->children;
211 // generate content if an array or arrayReference
212 if (mItemFactory->isArrayTag((char *)currNode->name)) {
213 while(childNode != NULL) {
214 if(childNode->type == XML_TEXT_NODE && childNode->content) {
215 const char * content = (char*)childNode->content;
216
217 // Determine if content is whitespace
218 bool whitespace = true;
219
220 const char * contentPtr = content;
221 // Step through to end of pointer
222 while(contentPtr != NULL) {
223 // If not a whitespace character, break
224 if(!isspace(*contentPtr++)) {
225 whitespace = false;
226 break;
227 }
228 }
229 if(!whitespace) {
230 std::string contentString(content);
231 boost::algorithm::trim(contentString);
232 itemProperties.insert(std::make_pair("Content", contentString));
233 itemProperties.insert(std::make_pair("XMLDir", mXMLDir));
234 break;
235 }
236 }
237 childNode = childNode->next;
238 }
239 }
240
241 // Pull attributes from node
242 xmlAttrPtr currAttribute = currNode->properties;
243 while(currAttribute != NULL) {
244 itemProperties.insert(std::make_pair((char *)currAttribute->name,
245 (char *)currAttribute->children->content));
246 currAttribute = currAttribute->next;
247 }
248
249 // Build XdmfItem
250 const std::vector<shared_ptr<XdmfItem> > childItems =
251 this->read(currNode->children);
252 shared_ptr<XdmfItem> newItem =
253 mItemFactory->createItem((const char *)currNode->name,
254 itemProperties,
255 childItems);
256
257 if(newItem == NULL) {
258 XdmfError::message(XdmfError::FATAL,
259 "mItemFactory failed to createItem in "
260 "XdmfCoreReader::XdmfCoreReaderImpl::readSingleNode");
261 }
262
263
264 // Populate built XdmfItem
265 newItem->populateItem(itemProperties,
266 childItems,
267 mCoreReader);
268
269 myItems.push_back(newItem);
270 mXPathMap.insert(std::make_pair(currNode, newItem));
271 }
272 }
273 }
274
275 void
readPathObjects(const std::string & xPath,std::vector<shared_ptr<XdmfItem>> & myItems)276 readPathObjects(const std::string & xPath,
277 std::vector<shared_ptr<XdmfItem> > & myItems)
278 {
279 xmlXPathObjectPtr xPathObject =
280 xmlXPathEvalExpression((xmlChar*)xPath.c_str(), mXPathContext);
281 if(xPathObject && xPathObject->nodesetval) {
282 for(int i=0; i<xPathObject->nodesetval->nodeNr; ++i) {
283 this->readSingleNode(xPathObject->nodesetval->nodeTab[i], myItems);
284 }
285 }
286 xmlXPathFreeObject(xPathObject);
287 }
288
289 xmlDocPtr mDocument;
290 std::map<std::string, xmlDocPtr> mDocuments;
291 const XdmfCoreReader * const mCoreReader;
292 const shared_ptr<const XdmfCoreItemFactory> mItemFactory;
293 std::string mXMLDir;
294 xmlXPathContextPtr mXPathContext;
295 std::map<xmlNodePtr, shared_ptr<XdmfItem> > mXPathMap;
296 };
297
XdmfCoreReader(const shared_ptr<const XdmfCoreItemFactory> itemFactory)298 XdmfCoreReader::XdmfCoreReader(const shared_ptr<const XdmfCoreItemFactory> itemFactory) :
299 mImpl(new XdmfCoreReaderImpl(itemFactory, this))
300 {
301 }
302
~XdmfCoreReader()303 XdmfCoreReader::~XdmfCoreReader()
304 {
305 delete mImpl;
306 }
307
308 XdmfItem *
DuplicatePointer(shared_ptr<XdmfItem> original) const309 XdmfCoreReader::DuplicatePointer(shared_ptr<XdmfItem> original) const
310 {
311 if (mImpl == NULL) {
312 XdmfError::message(XdmfError::FATAL, "Error: Reader Internal Object is NULL");
313 }
314 return mImpl->mItemFactory->DuplicatePointer(original);
315 }
316
317 std::vector<shared_ptr<XdmfHeavyDataController> >
generateHeavyDataControllers(std::map<std::string,std::string> controllerProperties,const std::vector<unsigned int> & passedDimensions,shared_ptr<const XdmfArrayType> passedArrayType,const std::string & passedFormat) const318 XdmfCoreReader::generateHeavyDataControllers(std::map<std::string, std::string> controllerProperties,
319 const std::vector<unsigned int> & passedDimensions,
320 shared_ptr<const XdmfArrayType> passedArrayType,
321 const std::string & passedFormat) const
322 {
323 return mImpl->mItemFactory->generateHeavyDataControllers(controllerProperties,
324 passedDimensions,
325 passedArrayType,
326 passedFormat);
327 }
328
329 shared_ptr<XdmfHeavyDataWriter>
generateHeavyDataWriter(std::string typeName,std::string path) const330 XdmfCoreReader::generateHeavyDataWriter(std::string typeName, std::string path) const
331 {
332 return mImpl->mItemFactory->generateHeavyDataWriter(typeName, path);
333 }
334
335 shared_ptr<XdmfItem >
parse(const std::string & lightData) const336 XdmfCoreReader::parse(const std::string & lightData) const
337 {
338 mImpl->parse(lightData);
339 const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
340 std::vector<shared_ptr<XdmfItem> > toReturn;
341 if(mImpl->mItemFactory->createItem((const char*)currNode->name,
342 std::map<std::string, std::string>(),
343 std::vector<shared_ptr<XdmfItem> >()) == NULL) {
344 toReturn = mImpl->read(currNode->children);
345 }
346 else {
347 toReturn = mImpl->read(currNode);
348 }
349 mImpl->closeFile();
350 return(toReturn[0]);
351 }
352
353 std::vector<shared_ptr<XdmfItem> >
readItems(const std::string & filePath) const354 XdmfCoreReader::readItems(const std::string & filePath) const
355 {
356 mImpl->openFile(filePath);
357 const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
358 const std::vector<shared_ptr<XdmfItem> > toReturn =
359 mImpl->read(currNode->children);
360 mImpl->closeFile();
361 return toReturn;
362 }
363
364 shared_ptr<XdmfItem>
read(const std::string & filePath) const365 XdmfCoreReader::read(const std::string & filePath) const
366 {
367 const std::vector<shared_ptr<XdmfItem> > toReturn = readItems(filePath);
368 if (toReturn.size() == 0) {
369 return(shared_ptr<XdmfItem>());
370 }
371 return(toReturn[0]);
372 }
373
374 std::vector<shared_ptr<XdmfItem> >
read(const std::string & filePath,const std::string & xPath) const375 XdmfCoreReader::read(const std::string & filePath,
376 const std::string & xPath) const
377 {
378 mImpl->openFile(filePath);
379 std::vector<shared_ptr<XdmfItem> > toReturn = this->readPathObjects(xPath);
380 mImpl->closeFile();
381 return toReturn;
382 }
383
384 std::vector<shared_ptr<XdmfItem> >
readPathObjects(const std::string & xPath) const385 XdmfCoreReader::readPathObjects(const std::string & xPath) const
386 {
387 std::vector<shared_ptr<XdmfItem> > toReturn;
388 mImpl->readPathObjects(xPath, toReturn);
389 return toReturn;
390 }
391
392 // C Wrappers
393
394 XDMFITEM *
XdmfCoreReaderRead(XDMFCOREREADER * reader,char * filePath,int * status)395 XdmfCoreReaderRead(XDMFCOREREADER * reader, char * filePath, int * status)
396 {
397 XDMF_ERROR_WRAP_START(status)
398 shared_ptr<XdmfItem> returnItem = ((XdmfCoreReader *)reader)->read(filePath);
399 return (XDMFITEM *)((void *)((XdmfItem *)((XdmfCoreReader *)reader)->DuplicatePointer(returnItem)));
400 XDMF_ERROR_WRAP_END(status)
401 return NULL;
402 }
403