XdmfCoreReader.cpp 13.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

Kenneth Leiter's avatar
Kenneth Leiter committed
24 25 26 27
#define vtk_libxml2_reference reference // Reversing VTK name mangling
#include <libxml/uri.h>
#include <libxml/xpointer.h>
#include <libxml/xmlreader.h>
28 29
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
30
#include <cstring>
31
#include <map>
32
#include <sstream>
33 34
#include <utility>
#include "XdmfArray.hpp"
35
#include "XdmfArrayType.hpp"
36 37
#include "XdmfCoreItemFactory.hpp"
#include "XdmfCoreReader.hpp"
38
#include "XdmfError.hpp"
39 40
#include "XdmfFunction.hpp"
#include "XdmfSubset.hpp"
41
#include "XdmfItem.hpp"
42
#include "XdmfSystemUtils.hpp"
43

44 45 46 47 48 49 50
/**
 * PIMPL
 */
class XdmfCoreReader::XdmfCoreReaderImpl {

public:

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

  ~XdmfCoreReaderImpl()
  {
  };

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

85
    mDocument = xmlReadFile(filePath.c_str(), NULL, XML_PARSE_NOENT);
86 87

    if(mDocument == NULL) {
88 89 90
      XdmfError::message(XdmfError::FATAL,
                         "xmlReadFile could not read " + filePath +
                         " in XdmfCoreReader::XdmfCoreReaderImpl::openFile");
91 92
    }

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

95 96 97 98
    mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
    mXPathMap.clear();
  }

99 100 101 102 103 104
  void
  parse(const std::string & lightData) 
  {
    mDocument = xmlParseDoc((const xmlChar*)lightData.c_str());
                               
    if(mDocument == NULL) {
105 106 107
      XdmfError::message(XdmfError::FATAL,
                         "xmlReadFile could not parse passed light data string"
                         " in XdmfCoreReader::XdmfCoreReaderImpl::parse");
108
    }
109 110
    
    //mDocuments.insert(std::make_pair((char*)mDocument->URL, mDocument));
111 112 113 114
    mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
    mXPathMap.clear();
  }

115 116 117 118 119
  /**
   * Constructs XdmfItems for all nodes in currNode's tree.
   * XdmfItems are constructed by recursively calling this function for all
   * children of currNode.
   */
120
  std::vector<shared_ptr<XdmfItem> >
121 122
  read(xmlNodePtr currNode)
  {
123
    std::vector<shared_ptr<XdmfItem> > myItems;
124 125 126

    while(currNode != NULL) {
      if(currNode->type == XML_ELEMENT_NODE) {
127 128
        // Normal reading
        this->readSingleNode(currNode, myItems);
129 130 131 132 133 134 135 136 137 138 139 140 141
      }
      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,
142
                 std::vector<shared_ptr<XdmfItem> > & myItems)
143
  {
144 145 146 147 148 149
    // Deal with proper resolution of XIncludes
    if(xmlStrcmp(currNode->name, (xmlChar*)"include") == 0) {
      
      xmlChar * xpointer = NULL;
      xmlChar * href = NULL;
      
150 151
      xmlAttrPtr currAttribute = currNode->properties;
      while(currAttribute != NULL) {
152 153 154 155 156 157
        if(xmlStrcmp(currAttribute->name, (xmlChar*)"xpointer") == 0) {
          xpointer = currAttribute->children->content;
        }
        if(xmlStrcmp(currAttribute->name, (xmlChar*)"href") == 0) {
          href = currAttribute->children->content;
        }
158 159
        currAttribute = currAttribute->next;
      }
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174
      xmlXPathContextPtr oldContext = mXPathContext;
      if(href) {
        xmlDocPtr document;
        xmlChar * filePath = xmlBuildURI(href, mDocument->URL);
        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;
        }
Kenneth Leiter's avatar
Kenneth Leiter committed
175
	xmlFree(filePath);
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
        mXPathContext = xmlXPtrNewContext(document, NULL, NULL);           
      }
      
      if(xpointer) {
        xmlXPathObjectPtr result = xmlXPtrEval(xpointer, mXPathContext);
        if(result && !xmlXPathNodeSetIsEmpty(result->nodesetval)) {
          for(int i=0; i<result->nodesetval->nodeNr; ++i) {
            this->readSingleNode(result->nodesetval->nodeTab[i],
                                 myItems);
          }
        }
        else {
          XdmfError::message(XdmfError::FATAL,
                             "Invalid xpointer encountered.");
        }
        xmlXPathFreeObject(result);
192
      }
193 194 195 196 197 198 199 200 201
      
      if(href) {
        xmlXPathFreeContext(mXPathContext);
      }
      
      mXPathContext = oldContext;
      
    }
    else {
202

203 204 205 206 207 208
      // Check to see if the node is already in the XPath Map (seen previously)
      std::map<xmlNodePtr, shared_ptr<XdmfItem> >::const_iterator iter =
        mXPathMap.find(currNode);
      // If it is grab it from the previously stored items
      if(iter != mXPathMap.end()) {
        myItems.push_back(iter->second);
209 210
      }
      else {
211 212
        // Otherwise, generate a new XdmfItem from the node
        std::map<std::string, std::string> itemProperties;
213

214
        xmlNodePtr childNode = currNode->children;
215
        // generate content if an array or arrayReference
216
        if (mItemFactory->isArrayTag((char *)currNode->name)) {
217 218
          while(childNode != NULL) {
            if(childNode->type == XML_TEXT_NODE && childNode->content) {
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
              const char * content = (char*)childNode->content;

              // Determine if content is whitespace
              bool whitespace = true;

              const char * contentPtr = content;
              // Step through to end of pointer
              while(contentPtr != NULL) {
                // If not a whitespace character, break
                if(!isspace(*contentPtr++)) {
                  whitespace = false;
                  break;
                }
              }
              if(!whitespace) {
234
                std::string contentString(content);
235
                boost::algorithm::trim(contentString);
236
                itemProperties.insert(std::make_pair("Content", contentString));
237 238 239
                itemProperties.insert(std::make_pair("XMLDir", mXMLDir));
                break;
              }
240 241 242 243
            }
            childNode = childNode->next;
          }
        }
244

245 246 247 248 249 250 251
        // Pull attributes from node
        xmlAttrPtr currAttribute = currNode->properties;
        while(currAttribute != NULL) {
          itemProperties.insert(std::make_pair((char *)currAttribute->name,
                                               (char *)currAttribute->children->content));
          currAttribute = currAttribute->next;
        }
252

253 254 255
        // Build XdmfItem
        const std::vector<shared_ptr<XdmfItem> > childItems =
          this->read(currNode->children);
256
        shared_ptr<XdmfItem> newItem =
257 258 259
          mItemFactory->createItem((const char *)currNode->name,
                                   itemProperties,
                                   childItems);
260

261
        if(newItem == NULL) {
262
          XdmfError::message(XdmfError::FATAL,
263 264 265
                             "mItemFactory failed to createItem in "
                             "XdmfCoreReader::XdmfCoreReaderImpl::readSingleNode");
        }
266

267 268

        // Populate built XdmfItem
269 270 271
        newItem->populateItem(itemProperties,
                              childItems,
                              mCoreReader);
272 273 274

        myItems.push_back(newItem);
        mXPathMap.insert(std::make_pair(currNode, newItem));
275 276 277 278 279 280
      }
    }
  }

  void
  readPathObjects(const std::string & xPath,
281
                  std::vector<shared_ptr<XdmfItem> > & myItems)
282 283 284 285 286
  {
    xmlXPathObjectPtr xPathObject =
      xmlXPathEvalExpression((xmlChar*)xPath.c_str(), mXPathContext);
    if(xPathObject && xPathObject->nodesetval) {
      for(int i=0; i<xPathObject->nodesetval->nodeNr; ++i) {
287
        this->readSingleNode(xPathObject->nodesetval->nodeTab[i], myItems);
288 289 290 291 292 293
      }
    }
    xmlXPathFreeObject(xPathObject);
  }

  xmlDocPtr mDocument;
294
  std::map<std::string, xmlDocPtr> mDocuments;
295
  const XdmfCoreReader * const mCoreReader;
296
  const shared_ptr<const XdmfCoreItemFactory> mItemFactory;
297 298
  std::string mXMLDir;
  xmlXPathContextPtr mXPathContext;
299
  std::map<xmlNodePtr, shared_ptr<XdmfItem> > mXPathMap;
300 301
};

302
XdmfCoreReader::XdmfCoreReader(const shared_ptr<const XdmfCoreItemFactory> itemFactory) :
303
  mImpl(new XdmfCoreReaderImpl(itemFactory, this))
304 305 306 307 308
{
}

XdmfCoreReader::~XdmfCoreReader()
{
309
  delete mImpl;
310 311
}

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
std::vector<shared_ptr<XdmfHeavyDataController> >
XdmfCoreReader::generateHeavyDataControllers(std::map<std::string, std::string> controllerProperties,
                                             const std::vector<unsigned int> & passedDimensions,
                                             shared_ptr<const XdmfArrayType> passedArrayType,
                                             const std::string & passedFormat) const
{
  return mImpl->mItemFactory->generateHeavyDataControllers(controllerProperties,
                                                           passedDimensions,
                                                           passedArrayType,
                                                           passedFormat);
}

shared_ptr<XdmfHeavyDataWriter>
XdmfCoreReader::generateHeavyDataWriter(std::string typeName, std::string path) const
{
  return mImpl->mItemFactory->generateHeavyDataWriter(typeName, path);
}

330 331 332
shared_ptr<XdmfItem >
XdmfCoreReader::parse(const std::string & lightData) const
{
333
  mImpl->parse(lightData);
334
  const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
335
  std::vector<shared_ptr<XdmfItem> > toReturn;
336 337
  if(mImpl->mItemFactory->createItem((const char*)currNode->name,
                                     std::map<std::string, std::string>(),
338
                                     std::vector<shared_ptr<XdmfItem> >()) == NULL) {
339
    toReturn = mImpl->read(currNode->children);
340
  }
341 342
  else {
    toReturn = mImpl->read(currNode);
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
  mImpl->openFile(filePath);
  const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
  const std::vector<shared_ptr<XdmfItem> > toReturn =
    mImpl->read(currNode->children);
  mImpl->closeFile();
  return toReturn;
357 358
}

359
shared_ptr<XdmfItem>
360
XdmfCoreReader::read(const std::string & filePath) const
361
{
362 363 364
  const std::vector<shared_ptr<XdmfItem> > toReturn = readItems(filePath);
  if (toReturn.size() == 0) {
    return(shared_ptr<XdmfItem>());
365
  }
366
  return(toReturn[0]);
367
}
368

369
std::vector<shared_ptr<XdmfItem> >
370 371
XdmfCoreReader::read(const std::string & filePath,
                     const std::string & xPath) const
372
{
373 374 375 376
  mImpl->openFile(filePath);
  std::vector<shared_ptr<XdmfItem> > toReturn = this->readPathObjects(xPath);
  mImpl->closeFile();
  return toReturn;
377
}
378

379
std::vector<shared_ptr<XdmfItem> >
380
XdmfCoreReader::readPathObjects(const std::string & xPath) const
381
{
382
  std::vector<shared_ptr<XdmfItem> > toReturn;
383
  mImpl->readPathObjects(xPath, toReturn);
384
  return toReturn;
385 386
}

387 388 389 390 391 392
// C Wrappers

XDMFITEM *
XdmfCoreReaderRead(XDMFCOREREADER * reader, char * filePath, int * status)
{
  XDMF_ERROR_WRAP_START(status)
Kenneth Leiter's avatar
Kenneth Leiter committed
393 394 395
  shared_ptr<XdmfCoreReader> & refReader = *(shared_ptr<XdmfCoreReader> *)(reader);
  shared_ptr<XdmfItem> * p = new shared_ptr<XdmfItem>(refReader->read(filePath));
  return (XDMFITEM *) p;
396 397 398
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}