00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #define FREPPLE_CORE
00028 #include "frepple/utils.h"
00029 #include <sys/stat.h>
00030
00031
00032
00033
00034
00035
00036 #ifdef _MSC_VER
00037 #define WIN32_LEAN_AND_MEAN
00038 #include <windows.h>
00039 #else
00040
00041 #if HAVE_DIRENT_H
00042 # include <dirent.h>
00043 # define NAMLEN(dirent) strlen((dirent)->d_name)
00044 #else
00045 # define dirent direct
00046 # define NAMLEN(dirent) (dirent)->d_namlen
00047 # if HAVE_SYS_NDIR_H
00048 # include <sys/ndir.h>
00049 # endif
00050 # if HAVE_SYS_DIR_H
00051 # include <sys/dir.h>
00052 # endif
00053 # if HAVE_NDIR_H
00054 # include <ndir.h>
00055 # endif
00056 #endif
00057 #endif
00058
00059
00060 namespace frepple
00061 {
00062 namespace utils
00063 {
00064
00065 DECLARE_EXPORT const XMLOutput::content_type XMLOutput::STANDARD = 1;
00066 DECLARE_EXPORT const XMLOutput::content_type XMLOutput::PLAN = 2;
00067 DECLARE_EXPORT const XMLOutput::content_type XMLOutput::PLANDETAIL = 4;
00068
00069
00070 void XMLInput::processingInstruction
00071 (const XMLCh *const target, const XMLCh *const data)
00072 {
00073 char* type = xercesc::XMLString::transcode(target);
00074 char* value = xercesc::XMLString::transcode(data);
00075 try
00076 {
00077
00078 const MetaClass* j = Command::metadataInstruction->findClass(type);
00079 if (!j || !j->processingInstruction)
00080 {
00081 string msg = string("Unknown processing instruction ") + type;
00082 xercesc::XMLString::release(&type);
00083 xercesc::XMLString::release(&value);
00084 throw LogicException(msg);
00085 }
00086 try
00087 {
00088
00089 j->processingInstruction(value);
00090 }
00091 catch (DataException e)
00092 {
00093 if (abortOnDataException)
00094 {
00095 xercesc::XMLString::release(&type);
00096 xercesc::XMLString::release(&value);
00097 throw;
00098 }
00099 else logger << "Continuing after data error: " << e.what() << endl;
00100 }
00101 xercesc::XMLString::release(&type);
00102 xercesc::XMLString::release(&value);
00103 }
00104 catch (...)
00105 {
00106 xercesc::XMLString::release(&type);
00107 xercesc::XMLString::release(&value);
00108 throw;
00109 }
00110 }
00111
00112
00113 void XMLInput::startElement(const XMLCh* const uri, const XMLCh* const n,
00114 const XMLCh* const qname, const xercesc::Attributes& atts)
00115 {
00116
00117 assert(!states.empty());
00118
00119
00120 if (numElements >= maxdepth)
00121 throw DataException("XML-document with elements nested excessively deep");
00122
00123
00124 datapair *pElement = &m_EStack[numElements+1];
00125 pElement->first.reset(n);
00126 pElement->second.reset();
00127
00128
00129 attributes = &atts;
00130
00131 switch (states.top())
00132 {
00133 case SHUTDOWN:
00134
00135
00136 return;
00137
00138 case IGNOREINPUT:
00139
00140 if (pElement->first.getHash() == endingHashes.top())
00141
00142 ++ignore;
00143 ++numElements;
00144 return;
00145
00146 case INIT:
00147
00148
00149 #ifdef PARSE_DEBUG
00150 if (getCurrentObject())
00151 logger << "Initialize root tag for reading object "
00152 << getCurrentObject() << " ("
00153 << typeid(*getCurrentObject()).name() << ")" << endl;
00154 else
00155 logger << "Initialize root tag for reading object NULL" << endl;
00156 #endif
00157 states.top() = READOBJECT;
00158 endingHashes.push(pElement->first.getHash());
00159
00160
00161
00162 case READOBJECT:
00163
00164
00165 #ifdef PARSE_DEBUG
00166 logger << " Start element " << pElement->first.getName()
00167 << " - object " << getCurrentObject() << endl;
00168 #endif
00169
00170
00171 assert(!m_EHStack.empty());
00172 try { getCurrentObject()->beginElement(*this, pElement->first); }
00173 catch (DataException e)
00174 {
00175 if (abortOnDataException) throw;
00176 else logger << "Continuing after data error: " << e.what() << endl;
00177 }
00178
00179
00180
00181 numElements += 1;
00182 if (states.top() != IGNOREINPUT)
00183 for (unsigned int i=0, cnt=atts.getLength(); i<cnt; i++)
00184 {
00185 char* val = xercesc::XMLString::transcode(atts.getValue(i));
00186 m_EStack[numElements+1].first.reset(atts.getLocalName(i));
00187 m_EStack[numElements+1].second.setData(val);
00188 #ifdef PARSE_DEBUG
00189 char* attname = xercesc::XMLString::transcode(atts.getQName(i));
00190 logger << " Processing attribute " << attname
00191 << " - object " << getCurrentObject() << endl;
00192 xercesc::XMLString::release(&attname);
00193 #endif
00194 try { getCurrentObject()->endElement(*this, m_EStack[numElements+1].first, m_EStack[numElements+1].second); }
00195 catch (DataException e)
00196 {
00197 if (abortOnDataException) throw;
00198 else logger << "Continuing after data error: " << e.what() << endl;
00199 }
00200 xercesc::XMLString::release(&val);
00201
00202 if (states.top() == IGNOREINPUT) break;
00203 }
00204 }
00205
00206
00207 attributes = NULL;
00208 }
00209
00210
00211 void XMLInput::endElement(const XMLCh* const uri,
00212 const XMLCh* const s,
00213 const XMLCh* const qname)
00214 {
00215
00216 assert(numElements >= 0);
00217 assert(!states.empty());
00218 assert(numElements < maxdepth);
00219
00220
00221 datapair *pElement = &(m_EStack[numElements--]);
00222
00223 switch (states.top())
00224 {
00225 case INIT:
00226
00227 throw LogicException("Unreachable code reached");
00228
00229 case SHUTDOWN:
00230
00231
00232 return;
00233
00234 case IGNOREINPUT:
00235
00236 #ifdef PARSE_DEBUG
00237 logger << " End element " << pElement->first.getName()
00238 << " - IGNOREINPUT state" << endl;
00239 #endif
00240
00241 if (pElement->first.getHash() != endingHashes.top()) return;
00242 if (ignore == 0)
00243 {
00244
00245 states.pop();
00246 endingHashes.pop();
00247 #ifdef PARSE_DEBUG
00248 logger << "Finish IGNOREINPUT state" << endl;
00249 #endif
00250 }
00251 else
00252 --ignore;
00253 break;
00254
00255 case READOBJECT:
00256
00257 #ifdef PARSE_DEBUG
00258 logger << " End element " << pElement->first.getName()
00259 << " - object " << getCurrentObject() << endl;
00260 #endif
00261
00262
00263 assert(!m_EHStack.empty());
00264 if (pElement->first.getHash() == endingHashes.top())
00265 {
00266
00267
00268 objectEnded = true;
00269 try { getCurrentObject()->endElement(*this, pElement->first, pElement->second); }
00270 catch (DataException e)
00271 {
00272 if (abortOnDataException) throw;
00273 else logger << "Continuing after data error: " << e.what() << endl;
00274 }
00275 objectEnded = false;
00276 #ifdef PARSE_DEBUG
00277 logger << "Finish reading object " << getCurrentObject() << endl;
00278 #endif
00279
00280 prev = getCurrentObject();
00281 m_EHStack.pop_back();
00282 endingHashes.pop();
00283
00284
00285 states.pop();
00286 if (m_EHStack.empty())
00287 shutdown();
00288 else
00289 {
00290
00291 try { getCurrentObject()->endElement(*this, pElement->first, pElement->second); }
00292 catch (DataException e)
00293 {
00294 if (abortOnDataException) throw;
00295 else logger << "Continuing after data error: " << e.what() << endl;
00296 }
00297 #ifdef PARSE_DEBUG
00298 logger << " End element " << pElement->first.getName()
00299 << " - object " << getCurrentObject() << endl;
00300 #endif
00301 }
00302 }
00303 else
00304
00305
00306 try { getCurrentObject()->endElement(*this, pElement->first, pElement->second); }
00307 catch (DataException e)
00308 {
00309 if (abortOnDataException) throw;
00310 else logger << "Continuing after data error: " << e.what() << endl;
00311 }
00312 }
00313 }
00314
00315
00316 void XMLInput::characters(const XMLCh *const c, const unsigned int n)
00317 {
00318
00319 if (states.top()==IGNOREINPUT) return;
00320
00321
00322 char* name = xercesc::XMLString::transcode(c);
00323 m_EStack[numElements].second.addData(name, strlen(name));
00324 xercesc::XMLString::release(&name);
00325 }
00326
00327
00328 void XMLInput::warning(const xercesc::SAXParseException& exception)
00329 {
00330 char* message = xercesc::XMLString::transcode(exception.getMessage());
00331 logger << "Warning: " << message
00332 << " at line: " << exception.getLineNumber() << endl;
00333 xercesc::XMLString::release(&message);
00334 }
00335
00336
00337 DECLARE_EXPORT void XMLInput::readto(Object * pPI)
00338 {
00339
00340 assert(numElements >= -1);
00341 endingHashes.push(m_EStack[numElements+1].first.getHash());
00342 if (pPI)
00343 {
00344
00345 #ifdef PARSE_DEBUG
00346 logger << "Start reading object " << pPI
00347 << " (" << typeid(*pPI).name() << ")" << endl;
00348 #endif
00349 prev = getCurrentObject();
00350 m_EHStack.push_back(make_pair(pPI,static_cast<void*>(NULL)));
00351 states.push(READOBJECT);
00352 }
00353 else
00354 {
00355
00356 #ifdef PARSE_DEBUG
00357 logger << "Start ignoring input" << endl;
00358 #endif
00359 states.push(IGNOREINPUT);
00360 }
00361 }
00362
00363
00364 void XMLInput::shutdown()
00365 {
00366
00367 if (states.empty() || states.top() == SHUTDOWN) return;
00368
00369
00370 #ifdef PARSE_DEBUG
00371 logger << " Forcing a shutdown - SHUTDOWN state" << endl;
00372 #endif
00373
00374
00375 states.push(SHUTDOWN);
00376
00377
00378 if (numElements<0) return;
00379
00380
00381
00382
00383 objectEnded = true;
00384 m_EStack[numElements].first.reset("Not a real tag");
00385 m_EStack[numElements].second.reset();
00386 while (!m_EHStack.empty())
00387 {
00388 try { getCurrentObject()->endElement(*this, m_EStack[numElements].first, m_EStack[numElements].second); }
00389 catch (DataException e)
00390 {
00391 if (abortOnDataException) throw;
00392 else logger << "Continuing after data error: " << e.what() << endl;
00393 }
00394 m_EHStack.pop_back();
00395 }
00396 }
00397
00398
00399 void XMLInput::reset()
00400 {
00401
00402 delete parser;
00403 parser = NULL;
00404
00405
00406
00407
00408 if (!m_EHStack.empty())
00409 {
00410
00411
00412
00413 if (objectEnded) m_EHStack.pop_back();
00414 objectEnded = true;
00415 m_EStack[++numElements].first.reset("Not a real tag");
00416 m_EStack[++numElements].second.reset();
00417 while (!m_EHStack.empty())
00418 {
00419 try { getCurrentObject()->endElement(*this, m_EStack[numElements].first, m_EStack[numElements].second); }
00420 catch (DataException e)
00421 {
00422 if (abortOnDataException) throw;
00423 else logger << "Continuing after data error: " << e.what() << endl;
00424 }
00425 m_EHStack.pop_back();
00426 }
00427 }
00428
00429
00430 while (!states.empty()) states.pop();
00431 while (!endingHashes.empty()) endingHashes.pop();
00432
00433
00434 numElements = -1;
00435 ignore = 0;
00436 objectEnded = false;
00437 attributes = NULL;
00438 }
00439
00440
00441 void XMLInput::parse(xercesc::InputSource &in, Object *pRoot, bool validate)
00442 {
00443 try
00444 {
00445
00446 parser = xercesc::XMLReaderFactory::createXMLReader();
00447
00448
00449
00450 parser->setProperty(xercesc::XMLUni::fgXercesScannerName, const_cast<XMLCh*>
00451 (validate ? xercesc::XMLUni::fgSGXMLScanner : xercesc::XMLUni::fgWFXMLScanner));
00452 parser->setFeature(xercesc::XMLUni::fgSAX2CoreValidation, validate);
00453 parser->setFeature(xercesc::XMLUni::fgSAX2CoreNameSpacePrefixes, false);
00454 parser->setFeature(xercesc::XMLUni::fgXercesIdentityConstraintChecking, false);
00455 parser->setFeature(xercesc::XMLUni::fgXercesDynamic, false);
00456 parser->setFeature(xercesc::XMLUni::fgXercesSchema, validate);
00457 parser->setFeature(xercesc::XMLUni::fgXercesSchemaFullChecking, false);
00458 parser->setFeature(xercesc::XMLUni::fgXercesValidationErrorAsFatal,true);
00459 parser->setFeature(xercesc::XMLUni::fgXercesIgnoreAnnotations,true);
00460
00461 if (validate)
00462 {
00463
00464 string schema = Environment::searchFile("frepple.xsd");
00465 if (schema.empty())
00466 throw RuntimeException("Can't find XML schema file 'frepple.xsd'");
00467 XMLCh *c = xercesc::XMLString::transcode(schema.c_str());
00468 parser->setProperty(
00469 xercesc::XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation, c
00470 );
00471 xercesc::XMLString::release(&c);
00472 }
00473
00474
00475
00476 if (pRoot)
00477 {
00478
00479
00480 parser->setContentHandler(this);
00481
00482
00483 m_EHStack.push_back(make_pair(pRoot,static_cast<void*>(NULL)));
00484 states.push(INIT);
00485 }
00486
00487
00488 parser->setErrorHandler(this);
00489
00490
00491 parser->parse(in);
00492 }
00493
00494
00495
00496 catch (const xercesc::XMLException& toCatch)
00497 {
00498 char* message = xercesc::XMLString::transcode(toCatch.getMessage());
00499 string msg(message);
00500 xercesc::XMLString::release(&message);
00501 reset();
00502 throw RuntimeException("Parsing error: " + msg);
00503 }
00504 catch (const xercesc::SAXParseException& toCatch)
00505 {
00506 char* message = xercesc::XMLString::transcode(toCatch.getMessage());
00507 ostringstream msg;
00508 if (toCatch.getLineNumber() > 0)
00509 msg << "Parsing error: " << message << " at line " << toCatch.getLineNumber();
00510 else
00511 msg << "Parsing error: " << message;
00512 xercesc::XMLString::release(&message);
00513 reset();
00514 throw RuntimeException(msg.str());
00515 }
00516 catch (const exception& toCatch)
00517 {
00518 reset();
00519 ostringstream msg;
00520 msg << "Error during XML parsing: " << toCatch.what();
00521 throw RuntimeException(msg.str());
00522 }
00523 catch (...)
00524 {
00525 reset();
00526 throw RuntimeException(
00527 "Parsing error: Unexpected exception during XML parsing");
00528 }
00529 reset();
00530
00531
00532
00533 executeCommands();
00534 }
00535
00536
00537 DECLARE_EXPORT ostream& operator << (ostream& os, const XMLEscape& x)
00538 {
00539 for (const char* p = x.data; *p; ++p)
00540 {
00541 switch (*p)
00542 {
00543 case '&': os << "&"; break;
00544 case '<': os << "<"; break;
00545 case '>': os << ">"; break;
00546 case '"': os << """; break;
00547 case '\'': os << "'"; break;
00548 default: os << *p;
00549 }
00550 }
00551 return os;
00552 }
00553
00554
00555 DECLARE_EXPORT void XMLOutput::incIndent()
00556 {
00557 indentstring[m_nIndent++] = '\t';
00558 if (m_nIndent > 40) m_nIndent = 40;
00559 indentstring[m_nIndent] = '\0';
00560 }
00561
00562
00563 DECLARE_EXPORT void XMLOutput::decIndent()
00564 {
00565 if (--m_nIndent < 0) m_nIndent = 0;
00566 indentstring[m_nIndent] = '\0';
00567 }
00568
00569
00570 DECLARE_EXPORT void XMLOutput::writeElement
00571 (const Keyword& tag, const Object* object, mode m)
00572 {
00573
00574 if (!object || object->getHidden()) return;
00575
00576
00577 const Object *previousParent = parentObject;
00578 parentObject = currentObject;
00579 currentObject = object;
00580 ++numObjects;
00581 ++numParents;
00582
00583
00584 if (m != DEFAULT)
00585
00586 object->writeElement(this, tag, m);
00587 else
00588
00589
00590 object->writeElement(this, tag, numParents>2 ? REFERENCE : DEFAULT);
00591
00592
00593 --numParents;
00594 currentObject = parentObject;
00595 parentObject = previousParent;
00596 }
00597
00598
00599 DECLARE_EXPORT void XMLOutput::writeElementWithHeader(const Keyword& tag, const Object* object)
00600 {
00601
00602 if (!object)
00603 throw RuntimeException("Can't accept a NULL object as XML root");
00604
00605
00606 if (numObjects > 0)
00607 throw LogicException("Can't have multiple headers in a document");
00608 assert(!parentObject);
00609 assert(!currentObject);
00610
00611
00612 writeString(getHeaderStart());
00613
00614
00615 currentObject = object;
00616
00617
00618 ++numObjects;
00619 ++numParents;
00620 BeginObject(tag, getHeaderAtts());
00621 object->writeElement(this, tag, NOHEADER);
00622
00623
00624 currentObject = NULL;
00625 parentObject = NULL;
00626 }
00627
00628
00629 DECLARE_EXPORT void XMLOutput::writeHeader(const Keyword& tag)
00630 {
00631
00632 if (numObjects > 0 || !parentObject || !currentObject)
00633 throw LogicException("Writing invalid header to XML document");
00634
00635
00636 writeString(getHeaderStart());
00637 BeginObject(tag, getHeaderAtts());
00638
00639
00640 numParents += 2;
00641 }
00642
00643
00644 DECLARE_EXPORT bool XMLElement::getBool() const
00645 {
00646 switch (getData()[0])
00647 {
00648 case 'T':
00649 case 't':
00650 case '1':
00651 return true;
00652 case 'F':
00653 case 'f':
00654 case '0':
00655 return false;
00656 }
00657 throw DataException("Invalid boolean value: " + string(getData()));
00658 }
00659
00660
00661 DECLARE_EXPORT const char* Attribute::getName() const
00662 {
00663 if (ch) return ch;
00664 Keyword::tagtable::const_iterator i = Keyword::getTags().find(hash);
00665 if (i == Keyword::getTags().end())
00666 throw LogicException("Undefined element keyword");
00667 return i->second->getName().c_str();
00668 }
00669
00670
00671 DECLARE_EXPORT Keyword::Keyword(const string& name) : strName(name)
00672 {
00673
00674 if (name.empty()) throw LogicException("Creating keyword without name");
00675
00676
00677 strStartElement = string("<") + name;
00678 strEndElement = string("</") + name + ">\n";
00679 strElement = string("<") + name + ">";
00680 strAttribute = string(" ") + name + "=\"";
00681
00682
00683 dw = hash(name.c_str());
00684
00685
00686 xercesc::XMLPlatformUtils::Initialize();
00687 xmlname = xercesc::XMLString::transcode(name.c_str());
00688
00689
00690 check();
00691 }
00692
00693
00694 DECLARE_EXPORT Keyword::Keyword(const string& name, const string& nspace)
00695 : strName(name)
00696 {
00697
00698 if (name.empty())
00699 throw LogicException("Creating keyword without name");
00700 if (nspace.empty())
00701 throw LogicException("Creating keyword with empty namespace");
00702
00703
00704 strStartElement = string("<") + nspace + ":" + name;
00705 strEndElement = string("</") + nspace + ":" + name + ">\n";
00706 strElement = string("<") + nspace + ":" + name + ">";
00707 strAttribute = string(" ") + nspace + ":" + name + "=\"";
00708
00709
00710 dw = hash(name);
00711
00712
00713 xercesc::XMLPlatformUtils::Initialize();
00714 xmlname = xercesc::XMLString::transcode(string(nspace + ":" + name).c_str());
00715
00716
00717 check();
00718 }
00719
00720
00721 void Keyword::check()
00722 {
00723
00724
00725 static Mutex dd;
00726 {
00727 ScopeMutexLock l(dd);
00728 tagtable::const_iterator i = getTags().find(dw);
00729 if (i!=getTags().end() && i->second->getName()!=strName)
00730 throw LogicException("Tag XML-tag hash function clashes for "
00731 + i->second->getName() + " and " + strName);
00732 getTags().insert(make_pair(dw,this));
00733 }
00734 }
00735
00736
00737 DECLARE_EXPORT Keyword::~Keyword()
00738 {
00739
00740 tagtable::iterator i = getTags().find(dw);
00741 if (i!=getTags().end()) getTags().erase(i);
00742
00743
00744 xercesc::XMLString::release(&xmlname);
00745 xercesc::XMLPlatformUtils::Terminate();
00746 }
00747
00748
00749 DECLARE_EXPORT const Keyword& Keyword::find(const char* name)
00750 {
00751 tagtable::const_iterator i = getTags().find(hash(name));
00752 return *(i!=getTags().end() ? i->second : new Keyword(name));
00753 }
00754
00755
00756 DECLARE_EXPORT Keyword::tagtable& Keyword::getTags()
00757 {
00758 static tagtable alltags;
00759 return alltags;
00760 }
00761
00762
00763 DECLARE_EXPORT void Keyword::printTags()
00764 {
00765 for (tagtable::iterator i = getTags().begin(); i != getTags().end(); ++i)
00766 logger << i->second->getName() << " " << i->second->dw << endl;
00767 }
00768
00769
00770 DECLARE_EXPORT void XMLInput::executeCommands()
00771 {
00772 try { cmds.execute(); }
00773 catch (...)
00774 {
00775 try { throw; }
00776 catch (exception& e)
00777 {logger << "Error executing commands: " << e.what() << endl;}
00778 catch (...)
00779 {logger << "Error executing commands: Unknown exception type" << endl;}
00780 throw;
00781 }
00782 }
00783
00784
00785 void XMLInputFile::parse(Object *pRoot, bool validate)
00786 {
00787
00788 if (filename.empty())
00789 throw DataException("Missing input file or directory");
00790
00791
00792 struct stat stat_p;
00793 if (stat(filename.c_str(), &stat_p))
00794
00795 throw RuntimeException("Couldn't open input file '" + filename + "'");
00796 else if (stat_p.st_mode & S_IFDIR)
00797 {
00798
00799
00800
00801 #ifdef _MSC_VER
00802 string f = filename + "\\*.xml";
00803 WIN32_FIND_DATA dir_entry_p;
00804 HANDLE h = FindFirstFile(f.c_str(), &dir_entry_p);
00805 if (h == INVALID_HANDLE_VALUE)
00806 throw RuntimeException("Couldn't open input file '" + f + "'");
00807 do
00808 {
00809 f = filename + '/' + dir_entry_p.cFileName;
00810 XMLInputFile(f.c_str()).parse(pRoot);
00811 }
00812 while (FindNextFile(h, &dir_entry_p));
00813 FindClose(h);
00814 #elif HAVE_DIRENT_H
00815 struct dirent *dir_entry_p;
00816 DIR *dir_p = opendir(filename.c_str());
00817 while (NULL != (dir_entry_p = readdir(dir_p)))
00818 {
00819 int n = NAMLEN(dir_entry_p);
00820 if (n > 4 && !strcmp(".xml", dir_entry_p->d_name + n - 4))
00821 {
00822 string f = filename + '/' + dir_entry_p->d_name;
00823 XMLInputFile(f.c_str()).parse(pRoot, validate);
00824 }
00825 }
00826 closedir(dir_p);
00827 #else
00828 throw RuntimeException("Can't process a directory on your platform");
00829 #endif
00830 }
00831 else
00832 {
00833
00834
00835 XMLCh *f = xercesc::XMLString::transcode(filename.c_str());
00836 xercesc::LocalFileInputSource in(f);
00837 xercesc::XMLString::release(&f);
00838 XMLInput::parse(in, pRoot, validate);
00839 }
00840 }
00841
00842 }
00843 }