XdmfCoreReader.cpp 13.6 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 25
#include <boost/algorithm/string/trim.hpp>
#include <boost/tokenizer.hpp>
26
#include <cstring>
27
#include <map>
28
#include <sstream>
29 30
#include <utility>
#include "XdmfArray.hpp"
31
#include "XdmfArrayType.hpp"
32 33
#include "XdmfCoreItemFactory.hpp"
#include "XdmfCoreReader.hpp"
34
#include "XdmfError.hpp"
35 36
#include "XdmfFunction.hpp"
#include "XdmfSubset.hpp"
37
#include "XdmfItem.hpp"
38
#include "XdmfSystemUtils.hpp"
39

40 41 42 43 44 45 46
/**
 * PIMPL
 */
class XdmfCoreReader::XdmfCoreReaderImpl {

public:

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

  ~XdmfCoreReaderImpl()
  {
  };

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

81
    mDocument = xmlReadFile(filePath.c_str(), NULL, XML_PARSE_NOENT);
82 83

    if(mDocument == NULL) {
84 85 86
      XdmfError::message(XdmfError::FATAL,
                         "xmlReadFile could not read " + filePath +
                         " in XdmfCoreReader::XdmfCoreReaderImpl::openFile");
87 88
    }

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

91 92 93 94
    mXPathContext = xmlXPtrNewContext(mDocument, NULL, NULL);
    mXPathMap.clear();
  }

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

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

    while(currNode != NULL) {
      if(currNode->type == XML_ELEMENT_NODE) {
123 124
        // Normal reading
        this->readSingleNode(currNode, myItems);
125 126 127 128 129 130 131 132 133 134 135 136 137
      }
      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,
138
                 std::vector<shared_ptr<XdmfItem> > & myItems)
139
  {
140 141 142 143 144 145
    // Deal with proper resolution of XIncludes
    if(xmlStrcmp(currNode->name, (xmlChar*)"include") == 0) {
      
      xmlChar * xpointer = NULL;
      xmlChar * href = NULL;
      
146 147
      xmlAttrPtr currAttribute = currNode->properties;
      while(currAttribute != NULL) {
148 149 150 151 152 153
        if(xmlStrcmp(currAttribute->name, (xmlChar*)"xpointer") == 0) {
          xpointer = currAttribute->children->content;
        }
        if(xmlStrcmp(currAttribute->name, (xmlChar*)"href") == 0) {
          href = currAttribute->children->content;
        }
154 155
        currAttribute = currAttribute->next;
      }
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
      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;
        }
        
        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);
188
      }
189 190 191 192 193 194 195 196 197
      
      if(href) {
        xmlXPathFreeContext(mXPathContext);
      }
      
      mXPathContext = oldContext;
      
    }
    else {
198

199 200 201 202 203 204
      // 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);
205 206
      }
      else {
207 208
        // Otherwise, generate a new XdmfItem from the node
        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 214
          while(childNode != NULL) {
            if(childNode->type == XML_TEXT_NODE && childNode->content) {
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
              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) {
230
                std::string contentString(content);
231
                boost::algorithm::trim(contentString);
232
                itemProperties.insert(std::make_pair("Content", contentString));
233 234 235
                itemProperties.insert(std::make_pair("XMLDir", mXMLDir));
                break;
              }
236 237 238 239
            }
            childNode = childNode->next;
          }
        }
240

241 242 243 244 245 246 247
        // 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;
        }
248

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

257
        if(newItem == NULL) {
258
          XdmfError::message(XdmfError::FATAL,
259 260 261
                             "mItemFactory failed to createItem in "
                             "XdmfCoreReader::XdmfCoreReaderImpl::readSingleNode");
        }
262

263 264

        // Populate built XdmfItem
265 266 267
        newItem->populateItem(itemProperties,
                              childItems,
                              mCoreReader);
268 269 270

        myItems.push_back(newItem);
        mXPathMap.insert(std::make_pair(currNode, newItem));
271 272 273 274 275 276
      }
    }
  }

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

  xmlDocPtr mDocument;
290
  std::map<std::string, xmlDocPtr> mDocuments;
291
  const XdmfCoreReader * const mCoreReader;
292
  const shared_ptr<const XdmfCoreItemFactory> mItemFactory;
293 294
  std::string mXMLDir;
  xmlXPathContextPtr mXPathContext;
295
  std::map<xmlNodePtr, shared_ptr<XdmfItem> > mXPathMap;
296 297
};

298
XdmfCoreReader::XdmfCoreReader(const shared_ptr<const XdmfCoreItemFactory> itemFactory) :
299
  mImpl(new XdmfCoreReaderImpl(itemFactory, this))
300 301 302 303 304
{
}

XdmfCoreReader::~XdmfCoreReader()
{
305
  delete mImpl;
306 307
}

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
XdmfItem *
XdmfCoreReader::DuplicatePointer(shared_ptr<XdmfItem> original) const
{
  if (mImpl == NULL) {
    XdmfError::message(XdmfError::FATAL, "Error: Reader Internal Object is NULL");
  }
  return mImpl->mItemFactory->DuplicatePointer(original);
}

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);
}

335 336 337
shared_ptr<XdmfItem >
XdmfCoreReader::parse(const std::string & lightData) const
{
338
  mImpl->parse(lightData);
339
  const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
340
  std::vector<shared_ptr<XdmfItem> > toReturn;
341 342
  if(mImpl->mItemFactory->createItem((const char*)currNode->name,
                                     std::map<std::string, std::string>(),
343
                                     std::vector<shared_ptr<XdmfItem> >()) == NULL) {
344
    toReturn = mImpl->read(currNode->children);
345
  }
346 347
  else {
    toReturn = mImpl->read(currNode);
348
  }
349 350 351 352
  mImpl->closeFile();
  return(toReturn[0]);
}

353
std::vector<shared_ptr<XdmfItem> >
354
XdmfCoreReader::readItems(const std::string & filePath) const
355
{
356 357 358 359 360 361
  mImpl->openFile(filePath);
  const xmlNodePtr currNode = xmlDocGetRootElement(mImpl->mDocument);
  const std::vector<shared_ptr<XdmfItem> > toReturn =
    mImpl->read(currNode->children);
  mImpl->closeFile();
  return toReturn;
362 363
}

364
shared_ptr<XdmfItem>
365
XdmfCoreReader::read(const std::string & filePath) const
366
{
367 368 369
  const std::vector<shared_ptr<XdmfItem> > toReturn = readItems(filePath);
  if (toReturn.size() == 0) {
    return(shared_ptr<XdmfItem>());
370
  }
371
  return(toReturn[0]);
372
}
373

374
std::vector<shared_ptr<XdmfItem> >
375 376
XdmfCoreReader::read(const std::string & filePath,
                     const std::string & xPath) const
377
{
378 379 380 381
  mImpl->openFile(filePath);
  std::vector<shared_ptr<XdmfItem> > toReturn = this->readPathObjects(xPath);
  mImpl->closeFile();
  return toReturn;
382
}
383

384
std::vector<shared_ptr<XdmfItem> >
385
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 393 394 395 396 397 398 399 400 401 402
// C Wrappers

XDMFITEM *
XdmfCoreReaderRead(XDMFCOREREADER * reader, char * filePath, int * status)
{
  XDMF_ERROR_WRAP_START(status)
  shared_ptr<XdmfItem> returnItem = ((XdmfCoreReader *)reader)->read(filePath);
  return (XDMFITEM *)((void *)((XdmfItem *)((XdmfCoreReader *)reader)->DuplicatePointer(returnItem)));
  XDMF_ERROR_WRAP_END(status)
  return NULL;
}