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