XdmfCoreReader.cpp 12.5 KB
Newer Older
Kenneth Leiter's avatar
Kenneth Leiter committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*****************************************************************************/
/*                                    XDMF                                   */
/*                       eXtensible Data Model and Format                    */
/*                                                                           */
/*  Id : XdmfCoreReader.cpp                                                  */
/*                                                                           */
/*  Author:                                                                  */
/*     Kenneth Leiter                                                        */
/*     kenneth.leiter@arl.army.mil                                           */
/*     US Army Research Laboratory                                           */
/*     Aberdeen Proving Ground, MD                                           */
/*                                                                           */
/*     Copyright @ 2011 US Army Research Laboratory                          */
/*     All Rights Reserved                                                   */
/*     See Copyright.txt for details                                         */
/*                                                                           */
/*     This software is distributed WITHOUT ANY WARRANTY; without            */
/*     even the implied warranty of MERCHANTABILITY or FITNESS               */
/*     FOR A PARTICULAR PURPOSE.  See the above copyright notice             */
/*     for more information.                                                 */
/*                                                                           */
/*****************************************************************************/
23

24
#include <libxml/uri.h>
25
#include <libxml/xpointer.h>
26
#include <libxml/xmlreader.h>
27 28 29
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
#include <cstring>
30
#include <map>
31
#include <sstream>
32 33
#include <utility>
#include "XdmfArray.hpp"
34 35
#include "XdmfArrayType.hpp"
#include "XdmfHDF5Controller.hpp"
36 37
#include "XdmfCoreItemFactory.hpp"
#include "XdmfCoreReader.hpp"
38
#include "XdmfError.hpp"
39
#include "XdmfItem.hpp"
40
#include "XdmfSystemUtils.hpp"
41

42 43 44 45 46 47 48
/**
 * PIMPL
 */
class XdmfCoreReader::XdmfCoreReaderImpl {

public:

49
  XdmfCoreReaderImpl(const shared_ptr<const XdmfCoreItemFactory> itemFactory,
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
                     const XdmfCoreReader * const coreReader) :
    mCoreReader(coreReader),
    mItemFactory(itemFactory)
  {
  };

  ~XdmfCoreReaderImpl()
  {
  };

  void
  closeFile()
  {
    mXPathMap.clear();
    xmlXPathFreeContext(mXPathContext);
65 66 67 68 69 70
    for(std::map<std::string, xmlDocPtr>::const_iterator iter = 
	  mDocuments.begin(); iter != mDocuments.end(); ++iter) {
      xmlFreeDoc(iter->second);
    }
    mDocuments.clear();
    
71 72 73 74 75 76 77 78 79 80 81 82
    xmlCleanupParser();
  }

  void
  openFile(const std::string & filePath)
  {
    mXMLDir = XdmfSystemUtils::getRealPath(filePath);
    size_t index = mXMLDir.find_last_of("/\\");
    if(index != std::string::npos) {
      mXMLDir = mXMLDir.substr(0, index + 1);
    }

83
    mDocument = xmlReadFile(filePath.c_str(), NULL, XML_PARSE_NOENT);
84 85

    if(mDocument == NULL) {
86 87 88 89 90 91 92 93
      try {
        XdmfError::message(XdmfError::FATAL,
                           "xmlReadFile could not read " + filePath +
                           " in XdmfCoreReader::XdmfCoreReaderImpl::openFile");
      }
      catch (XdmfError e) {
        throw e;
      }
94 95
    }

96 97
    mDocuments.insert(std::make_pair((char*)mDocument->URL, mDocument));

98 99 100 101
    mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
    mXPathMap.clear();
  }

102 103 104 105 106 107
  void
  parse(const std::string & lightData) 
  {
    mDocument = xmlParseDoc((const xmlChar*)lightData.c_str());
                               
    if(mDocument == NULL) {
108 109 110 111 112 113 114 115
      try {
        XdmfError::message(XdmfError::FATAL,
                           "xmlReadFile could not parse passed light data string"
                           " in XdmfCoreReader::XdmfCoreReaderImpl::parse");
      }
      catch (XdmfError e) {
        throw e;
      }
116 117
    }

118
    //    mDocuments.insert(std::make_pair((char*)mDocument->URL, mDocument));
119 120 121 122
    mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
    mXPathMap.clear();
  }

123 124 125 126 127
  /**
   * Constructs XdmfItems for all nodes in currNode's tree.
   * XdmfItems are constructed by recursively calling this function for all
   * children of currNode.
   */
128
  std::vector<shared_ptr<XdmfItem> >
129 130
  read(xmlNodePtr currNode)
  {
131
    std::vector<shared_ptr<XdmfItem> > myItems;
132 133 134 135 136

    while(currNode != NULL) {
      if(currNode->type == XML_ELEMENT_NODE) {
        if(xmlStrcmp(currNode->name, (xmlChar*)"include") == 0) {
          // Deal with proper reading of XIncludes
137 138 139 140

          xmlChar * xpointer = NULL;
          xmlChar * href = NULL;

141 142 143 144 145
          xmlAttrPtr currAttribute = currNode->properties;
          while(currAttribute != NULL) {
            if(xmlStrcmp(currAttribute->name, (xmlChar*)"xpointer") == 0) {
              xpointer = currAttribute->children->content;
            }
146 147 148 149 150 151 152 153
            if(xmlStrcmp(currAttribute->name, (xmlChar*)"href") == 0) {
              href = currAttribute->children->content;
            }
            currAttribute = currAttribute->next;
          }

          xmlXPathContextPtr context = mXPathContext;
          if(href) {
154
	    xmlDocPtr document;
155
            xmlChar * filePath = xmlBuildURI(href, mDocument->URL);
156 157 158 159 160 161 162 163 164 165
	    std::map<std::string, xmlDocPtr>::const_iterator iter = 
	      mDocuments.find((char*)filePath);
	    if(iter == mDocuments.end()) {
	      document = xmlReadFile((char*)filePath, NULL, 0);
	      mDocuments.insert(std::make_pair((char*)document->URL, document));
	    }
	    else {
	      document = iter->second;
	    }

166
            context = xmlXPtrNewContext(document, NULL, NULL);           
167
          }
168

169
          if(xpointer) {
170 171 172 173
            xmlXPathObjectPtr result = xmlXPtrEval(xpointer, context);
            if(result && !xmlXPathNodeSetIsEmpty(result->nodesetval)) {
              for(int i=0; i<result->nodesetval->nodeNr; ++i) {
                this->readSingleNode(result->nodesetval->nodeTab[i],
174 175 176
                                     myItems);
              }
            }
177 178 179 180 181
            xmlXPathFreeObject(result);
          }

          if(href) {
            xmlXPathFreeContext(context);
182
          }
183

184 185 186
        }
        else {
          // Normal reading
187 188 189 190 191 192
          try {
            this->readSingleNode(currNode, myItems);
          }
          catch (XdmfError e) {
            throw e;
          }
193 194 195 196 197 198 199 200 201 202 203 204 205 206
        }
      }
      currNode = currNode->next;
    }
    return myItems;
  }

  /**
   * Reads a single xmlNode into an XdmfItem object in memory. The constructed
   * XdmfItem is added to myItems and an entry is added mapping the xmlNodePtr
   * to the new XdmfItem in the mXPathMap.
   */
  void
  readSingleNode(const xmlNodePtr currNode,
207
                 std::vector<shared_ptr<XdmfItem> > & myItems)
208
  {
209
    // Check to see if the node is already in the XPath Map (seen previously)
210
    std::map<xmlNodePtr, shared_ptr<XdmfItem> >::const_iterator iter =
211
      mXPathMap.find(currNode);
212
    // If it is grab it from the previously stored items
213
    if(iter != mXPathMap.end()) {
214 215
      myItems.push_back(iter->second);
    }
216
    else {
217
      // Otherwise, generate a new Item from the node
218
      std::map<std::string, std::string> itemProperties;
219 220

      xmlNodePtr childNode = currNode->children;
221 222
      if (XdmfArray::ItemTag.compare((char *)currNode->name) == 0 ||
          strcmp("DataStructure", (char *)currNode->name) == 0) {
223 224 225
        while(childNode != NULL) {
          if(childNode->type == XML_TEXT_NODE && childNode->content) {
            
226 227
            std::string content((char *)childNode->content);
            boost::algorithm::trim(content);
228
            
229
            if(content.size() != 0) {
230 231 232
              itemProperties.insert(std::make_pair("Content", content));
              itemProperties.insert(std::make_pair("XMLDir", mXMLDir));
              break;
233 234
            }
          }
235
          childNode = childNode->next;
236
        }
237
      }
238
    
239 240
      xmlAttrPtr currAttribute = currNode->properties;
      while(currAttribute != NULL) {
241 242
        itemProperties.insert(std::make_pair((char *)currAttribute->name,
                                             (char *)currAttribute->children->content));
243 244
        currAttribute = currAttribute->next;
      }
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
      try {
        const std::vector<shared_ptr<XdmfItem> > childItems =
          this->read(currNode->children);
        shared_ptr<XdmfItem> newItem = 
          mItemFactory->createItem((const char *)currNode->name,
                                   itemProperties,
                                   childItems);
      
        if(newItem == NULL) {
          try {
            XdmfError::message(XdmfError::FATAL, 
                               "mItemFactory failed to createItem in "
                               "XdmfCoreReader::XdmfCoreReaderImpl::readSingleNode");
          }
          catch (XdmfError e) {
            throw e;
          }
        }
263
        if (newItem->getItemTag().compare((const char *)currNode->name) != 0) {
264 265 266
          newItem->populateItem(itemProperties,
                                std::vector<shared_ptr<XdmfItem> >(),
                                mCoreReader);
267 268
        }
        else {
269 270 271
          newItem->populateItem(itemProperties,
          childItems,
          mCoreReader);
272
        }
273 274 275 276 277
        myItems.push_back(newItem);
        mXPathMap.insert(std::make_pair(currNode, newItem));
      }
      catch (XdmfError e) {
        throw e;
278 279 280 281 282 283
      }
    }
  }

  void
  readPathObjects(const std::string & xPath,
284
                  std::vector<shared_ptr<XdmfItem> > & myItems)
285 286 287 288 289
  {
    xmlXPathObjectPtr xPathObject =
      xmlXPathEvalExpression((xmlChar*)xPath.c_str(), mXPathContext);
    if(xPathObject && xPathObject->nodesetval) {
      for(int i=0; i<xPathObject->nodesetval->nodeNr; ++i) {
290 291 292 293 294 295
        try {
          this->readSingleNode(xPathObject->nodesetval->nodeTab[i], myItems);
        }
        catch (XdmfError e) {
          throw e;
        }
296 297 298 299 300 301
      }
    }
    xmlXPathFreeObject(xPathObject);
  }

  xmlDocPtr mDocument;
302
  std::map<std::string, xmlDocPtr> mDocuments;
303
  const XdmfCoreReader * const mCoreReader;
304
  const shared_ptr<const XdmfCoreItemFactory> mItemFactory;
305 306
  std::string mXMLDir;
  xmlXPathContextPtr mXPathContext;
307
  std::map<xmlNodePtr, shared_ptr<XdmfItem> > mXPathMap;
308 309
};

310
XdmfCoreReader::XdmfCoreReader(const shared_ptr<const XdmfCoreItemFactory> itemFactory) :
311
  mImpl(new XdmfCoreReaderImpl(itemFactory, this))
312 313 314 315 316
{
}

XdmfCoreReader::~XdmfCoreReader()
{
317
  delete mImpl;
318 319
}

320 321 322
shared_ptr<XdmfItem >
XdmfCoreReader::parse(const std::string & lightData) const
{
323 324 325 326 327 328
  try {
    mImpl->parse(lightData);
  }
  catch (XdmfError e) {
    throw e;
  }
329
  const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
330
  std::vector<shared_ptr<XdmfItem> > toReturn;
331 332 333 334 335 336 337 338 339
  try {
    if(mImpl->mItemFactory->createItem((const char*)currNode->name,
                                       std::map<std::string, std::string>(),
                                       std::vector<shared_ptr<XdmfItem> >()) == NULL) {
      toReturn = mImpl->read(currNode->children);
    }
    else {
      toReturn = mImpl->read(currNode);
    }
340
  }
341 342
  catch (XdmfError e) {
    throw e;
343
  }
344 345 346 347
  mImpl->closeFile();
  return(toReturn[0]);
}

348
std::vector<shared_ptr<XdmfItem> >
349
XdmfCoreReader::readItems(const std::string & filePath) const
350
{
351 352 353 354 355 356 357 358 359 360 361 362
  try {
    mImpl->openFile(filePath);
    const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
    const std::vector<shared_ptr<XdmfItem> > toReturn =
      mImpl->read(currNode->children);
    mImpl->closeFile();
    return toReturn;
  }
  catch (XdmfError e) {
    throw e;
  }

363 364
}

365
shared_ptr<XdmfItem>
366
XdmfCoreReader::read(const std::string & filePath) const
367
{
368 369 370 371 372 373 374 375 376
  try {
    const std::vector<shared_ptr<XdmfItem> > toReturn = readItems(filePath);
    if (toReturn.size() == 0) {
      return(shared_ptr<XdmfItem>());
    }
    return(toReturn[0]);
  }
  catch (XdmfError e) {
    throw e;
377
  }
378
}
379

380
std::vector<shared_ptr<XdmfItem> >
381 382
XdmfCoreReader::read(const std::string & filePath,
                     const std::string & xPath) const
383
{
384 385 386 387 388 389 390 391 392
  try {
    mImpl->openFile(filePath);
    std::vector<shared_ptr<XdmfItem> > toReturn = this->readPathObjects(xPath);
    mImpl->closeFile();
    return toReturn;
  }
  catch (XdmfError e) {
    throw e;
  }
393
}
394

395
std::vector<shared_ptr<XdmfItem> >
396
XdmfCoreReader::readPathObjects(const std::string & xPath) const
397
{
398
  std::vector<shared_ptr<XdmfItem> > toReturn;
399 400 401 402 403 404
  try {
    mImpl->readPathObjects(xPath, toReturn);
  }
  catch (XdmfError e) {
    throw e;
  }
405
  return toReturn;
406 407
}