00001 // See ../../license.txt for license information. 00002 // 00003 // parse.cpp 00004 // 00005 // NOTES 00006 // XML Parser for the persistence framework. 00007 // 00008 // 30-Jun-2003 phamilton Created 00009 // 00010 00011 #define PERSIST_IN_LIBRARY_SOURCE 00012 00013 #include <iostream> 00014 #include <fstream> 00015 #include <unistd.h> 00016 #include <boost/lexical_cast.hpp> 00017 #include <boost/filesystem/path.hpp> 00018 #include <boost/filesystem/operations.hpp> 00019 #include <boost/bind.hpp> 00020 #include "parseobj.hpp" 00021 #include "../../common/object.hpp" 00022 #include "../../common/composition_object.hpp" 00023 #include "../../common/persistable_object.hpp" 00024 #include "../../common/outerable_object.hpp" 00025 #include "../../common/importable_object.hpp" 00026 00027 using namespace ph::persist; 00028 00029 // important strings 00030 const xmlstring s_objtype = S("type"); 00031 const xmlstring s_objname = S("name"); 00032 00033 // possible errors. 00034 const std::string efmt_bad_stack = S("Expected an entry on the object stack."); 00035 const std::string efmt_bad_tag = S("Unknown tag [%s]."); 00036 const std::string efmt_fail_create = S("Couldn't create object [%s (%s)]."); 00037 const std::string efmt_fail_create_root = S("Couldn't create root object [%s (%s)]."); 00038 const std::string efmt_fail_create_delayed = S("Couldn't create delayed object [%s (%s)]."); 00039 const std::string efmt_fail_not_importable = S("Object created was not importable."); 00040 const std::string efmt_prop_not_comp = S("Member is not composite object [%s(%s)]."); 00041 const std::string efmt_bad_aggr_obj = S("Composite did not accept object (wrong type?) [%s(%s)]."); 00042 const std::string efmt_bad_import = S("Could not import [%s]."); 00043 const std::string efmt_bad_stream = S("Could not create URL stream for [%s]."); 00044 const std::string efmt_bad_urlstream = S("Could not open URL stream for [%s]."); 00045 const std::string efmt_bad_endimport = S("Bad place for an end import tag [%s]."); 00046 const std::string efmt_bad_fragment = S("Bad place for a fragment tag [%s]."); 00047 const std::string efmt_bad_endfragment = S("Bad place for an end fragment tag [%s]."); 00048 const std::string efmt_bad_location = S("Location not found for a fragment tag [%s]."); 00049 const std::string efmt_no_interface = S("Object has no %s interface [%s]."); 00050 const std::string efmt_not_composite = S("Object is not composite [%s]."); 00051 const std::string efmt_bad_data = S("Tried to set a member [%s] with bad data [%s]."); 00052 const std::string efmt_bad_path = S("Bad pathvar encountered [%s] with [%s]."); 00053 const std::string efmt_bad_ref_location = S("Location not found for a reference [%s]."); 00054 const std::string efmt_end_stack = S("At the end, the stack still had elements. top: [%s]. " 00055 "Check for unbalanced tags."); 00056 const std::string efmt_import_is_root = S("An import is at the root of the file."); 00057 00058 xml::parseobj::parseobj(parseobj *outer, std::istream *stream, const boost::filesystem::path &streampath, 00059 ph::common::persistable_object_context *context, 00060 std::ostream *console, const boost::filesystem::path &rootpath, bool silent, unsigned int debug) : 00061 parse(console, silent) 00062 { 00063 _outer = outer; 00064 _stream = stream; 00065 00066 // make sure that the streampath passed in is complete. 00067 _streampath = boost::filesystem::system_complete(streampath); 00068 _context = context; 00069 _rootpath = rootpath; 00070 _debug = debug; 00071 00072 _obj = 0; 00073 _root = 0; 00074 _rootouter = 0; 00075 _cdata = ""; 00076 _comment = ""; 00077 _abort = false; 00078 _inmember = false; 00079 _anonymousobjnum = 0; 00080 _import = 0; 00081 _rootname = ""; 00082 _fragment = false; 00083 _delayed_obj = 0; 00084 } 00085 00086 static std::string obj_name(ph::common::object_base *obj) 00087 { 00088 std::string name; 00089 if (get_persistable_obj_name(obj, &name)) 00090 return name; 00091 else 00092 return "No name for object"; 00093 } 00094 00095 static std::string obj_type(ph::common::object_base *obj) 00096 { 00097 std::string type; 00098 if (get_persistable_obj_type(obj, &type)) 00099 return type; 00100 else 00101 return "No type for object"; 00102 } 00103 00104 void xml::parseobj::finish_handler() 00105 { 00106 // we have finished. Make sure that the stack is empty and that we have successfully 00107 // stashed an object. 00108 if (!_abort && !_stack.empty()) 00109 error(efmt_end_stack, obj_name(_stack.top()), true); 00110 } 00111 00112 void xml::parseobj::startelement_handler(const xmlstring &element, const std::vector<xmlstring> &atts) 00113 { 00114 if (_abort) 00115 return; 00116 00117 if (_debug & (PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00118 *_errorhandler << ">> startelement : [" << element << "]" << std::endl; 00119 00120 xmlstring arg = attr(atts, 0); 00121 00122 if (arg.empty()) 00123 { 00124 if (_stack.empty()) 00125 { 00126 error(efmt_bad_stack, true); 00127 _abort = true; 00128 return; 00129 } 00130 00131 ph::common::object_base *top = _stack.top(); 00132 if (!test_persistable(top)) 00133 return; 00134 00135 if (top->persistable()->has(element)) 00136 { 00137 // the top has this as an element, so see if it's a composite element, 00138 // then push that, or we are starting a member, 00139 ph::common::object_base *c = top->persistable()->get_composite_object(element); 00140 if (c) 00141 { 00142 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00143 obj_out("pushing", c); 00144 _stack.push(c); 00145 } 00146 else 00147 { 00148 // a normal member 00149 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00150 element_out("in member", element); 00151 _inmember = true; 00152 } 00153 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00154 *_errorhandler << ">> emptying cdata." << std::endl; 00155 _cdata = ""; 00156 } 00157 else 00158 { 00159 // it's possible that we are trying to build an unnamed ph::common::object_base. 00160 ph::common::object_base *obj = create_object(_stack.top(), element, 00161 S("_") + boost::lexical_cast<std::string>(_anonymousobjnum++)); 00162 if (!obj) 00163 { 00164 error(efmt_bad_tag, element, true); 00165 _abort = true; 00166 return; 00167 } 00168 00169 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00170 { 00171 obj_out("composite (1)", _stack.top()); 00172 obj_out("adding to composite (1)", obj); 00173 } 00174 if (!test_composition(_stack.top())) 00175 return; 00176 if (!_stack.top()->composition()->composite()->add(obj, true)) 00177 { 00178 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true); 00179 _abort = true; 00180 return; 00181 } 00182 00183 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00184 obj_out("pushing anonymous", obj); 00185 _stack.push(obj); 00186 } 00187 } 00188 else if (_import) 00189 { 00190 // in the middle of an import. 00191 // we can expect... 00192 // name value= 00193 // param name= value 00194 00195 if (element == "name") 00196 _import->_rootname = expectedattr(atts, "value"); 00197 else if (element == "param") 00198 { 00199 // add parameter, do subs on the name and the value. 00200 _import->add_param( 00201 dosubs(expectedattr(atts, "name")), 00202 dosubs(expectedattr(atts, "value"))); 00203 } 00204 else if (element == "pathparam") 00205 { 00206 // add parameter, do subs on the name and the value 00207 // and push them back. We convert relative paths 00208 // to absolute. 00209 std::string pathstr = dosubs(expectedattr(atts, "value")); 00210 00211 try 00212 { 00213 boost::filesystem::path p = pathstr; 00214 if (!p.is_complete()) 00215 p = _streampath.branch_path() / p; 00216 _import->add_param(dosubs(expectedattr(atts, "name")), p.string()); 00217 } 00218 catch (...) 00219 { 00220 error(efmt_bad_path, element, pathstr, true); 00221 _abort = true; 00222 return; 00223 } 00224 } 00225 } 00226 else if (_delayed_obj) 00227 { 00228 // in the middle of an delayed import. 00229 // we can expect... 00230 // name value= 00231 // param name= value 00232 00233 if (element == "name") 00234 { 00235 // set the root name for the import object. 00236 _delayed_obj->setname(expectedattr(atts, "value")); 00237 } 00238 else if (element == "param") 00239 { 00240 // add parameter, do subs on the name and the value. 00241 _delayed_obj->addparam( 00242 dosubs(expectedattr(atts, "name")), 00243 dosubs(expectedattr(atts, "value"))); 00244 } 00245 else if (element == "pathparam") 00246 { 00247 // add parameter, do subs on the name and the value 00248 // and push them back. We convert relative paths 00249 // to absolute. 00250 std::string pathstr = dosubs(expectedattr(atts, "value")); 00251 00252 try 00253 { 00254 boost::filesystem::path p = pathstr; 00255 if (!p.is_complete()) 00256 p = _streampath.branch_path() / p; 00257 _delayed_obj->addparam(dosubs(expectedattr(atts, "name")), p.string()); 00258 } 00259 catch (...) 00260 { 00261 error(efmt_bad_path, element, pathstr, true); 00262 _abort = true; 00263 return; 00264 } 00265 } 00266 } 00267 else 00268 { 00269 // an element with attributes 00270 // now check the tag. 00271 if (element == S("import") || element == S("xi:include")) 00272 { 00273 xmlstring url = attr(atts, "href"); 00274 // allow for href or url. 00275 if (url.empty()) 00276 url = expectedattr(atts, "url"); 00277 xmlstring name = attr(atts, "name"); 00278 00279 // try any string substitutions on the value (for $() stuff). 00280 url = dosubs(url); 00281 name = dosubs(name); 00282 00283 if (_context->delayed_import()) 00284 { 00285 // we want to import the XML passed. 00286 // create a substream of the current stream for the import. 00287 boost::filesystem::path p(url, boost::filesystem::no_check); 00288 if (!p.is_complete()) 00289 p = _streampath.branch_path() / p; 00290 00291 // create a proxy object to handle parseing during import. 00292 ph::common::object_base *obj = _context->create_delayed(_errorhandler, 00293 p, name, _debug); 00294 if (obj) 00295 { 00296 // delayed objects need an importable interface, since 00297 // our XML files can have names and parameters. 00298 _delayed_obj = obj->importable(); 00299 if (!_delayed_obj) 00300 { 00301 error(efmt_fail_not_importable, true); 00302 _abort = true; 00303 return; 00304 } 00305 } 00306 else 00307 { 00308 error(efmt_fail_create_delayed, url, name, true); 00309 _abort = true; 00310 return; 00311 } 00312 00313 if (_stack.empty()) 00314 { 00315 error(efmt_import_is_root, true); 00316 _abort = true; 00317 return; 00318 } 00319 else 00320 { 00321 // if the object on the top of the stack is a composite, then add this object to that. 00322 if (!test_composition(_stack.top())) 00323 return; 00324 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00325 { 00326 obj_out("composite (2)", _stack.top()); 00327 obj_out("adding to composite (2)", obj); 00328 } 00329 if (!_stack.top()->composition()->composite()->add(obj, true)) 00330 { 00331 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true); 00332 _abort = true; 00333 return; 00334 } 00335 } 00336 if (_debug & PARSEOBJ_DELAYED_DEBUG) 00337 *_errorhandler << ">> [" << p.string() << "] delaying." << std::endl; 00338 _stack.push(obj); 00339 } 00340 else 00341 { 00342 if (_import) 00343 { 00344 error(efmt_bad_import, url, true); 00345 _abort = true; 00346 return; 00347 } 00348 00349 // we want to import the XML passed. 00350 // create a substream of the current stream for the import. 00351 boost::filesystem::path p(url, boost::filesystem::no_check); 00352 _importstreampath = p; 00353 if (!_importstreampath.is_complete()) 00354 _importstreampath = _streampath.branch_path() / _importstreampath; 00355 _importstream = new std::ifstream(_importstreampath.native_file_string().c_str()); 00356 00357 // create a new importer. 00358 _import = new parseobj(this, _importstream, _importstreampath, _context, 00359 _errorhandler, _rootpath, _silent, _debug); 00360 00361 if (name != "") 00362 _import->_rootname = name; 00363 } 00364 } 00365 else if (element == S("fragment")) 00366 { 00367 xmlstring location = expectedattr(atts, "location"); 00368 if (_fragment) 00369 { 00370 error(efmt_bad_fragment, location, true); 00371 _abort = true; 00372 return; 00373 } 00374 00375 ph::common::object_base *c = _context->find_composite_object(_root, _root, location); 00376 if (!c) 00377 { 00378 error(efmt_bad_location, location, true); 00379 _abort = true; 00380 return; 00381 } 00382 00383 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00384 obj_out("pushing composite", c); 00385 _stack.push(c); 00386 _fragment = true; 00387 } 00388 else if (element == S("var")) 00389 { 00390 // do subs on the name and the value. 00391 // and push them back. 00392 add_param( 00393 dosubs(expectedattr(atts, "name")), 00394 dosubs(expectedattr(atts, "value"))); 00395 } 00396 else if (element == S("pathvar")) 00397 { 00398 // do subs on the name and the value. 00399 // and push them back. We convert relative paths 00400 // to absolute. 00401 std::string pathstr = dosubs(expectedattr(atts, "value")); 00402 00403 try 00404 { 00405 boost::filesystem::path p = pathstr; 00406 if (!p.is_complete()) 00407 p = _streampath.branch_path() / p; 00408 add_param(dosubs(expectedattr(atts, "name")), p.string()); 00409 } 00410 catch (...) 00411 { 00412 error(efmt_bad_path, element, pathstr, true); 00413 _abort = true; 00414 return; 00415 } 00416 } 00417 else 00418 { 00419 if (arg == s_objname) 00420 { 00421 xmlstring val = attrval(atts, 0); 00422 00423 // starting an object. name is given. 00424 ph::common::object_base *obj = 0; 00425 00426 if (_stack.empty()) 00427 { 00428 // create the new object. 00429 // it's possible that we arer in the middle of parseing 00430 // some NEW objects into the middle of another file. 00431 00432 // If there is an outer stack, then ask THAT object to create a new one, 00433 // otherwise just use the context. 00434 if (_outer && !_outer->_stack.empty()) 00435 obj = create_object(_outer->_stack.top(), element, val); 00436 00437 if (!obj) 00438 obj = _context->create(element, val); 00439 00440 if (!obj) 00441 { 00442 error(efmt_fail_create_root, element, val, true); 00443 _abort = true; 00444 return; 00445 } 00446 00447 // save away this object as the root object. 00448 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00449 obj_out("stashing as root", obj); 00450 00451 _root = obj; 00452 00453 // root objects can have an outer as well. This is especially 00454 // true in the case of delayed importing. 00455 if (_rootouter) 00456 if (_root->outerable()) 00457 _root->outerable()->outer(_rootouter); 00458 00459 // set the filepath for the root object. 00460 if (!test_persistable(_root)) 00461 return; 00462 _root->persistable()->set_file_path(_streampath.string()); 00463 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00464 obj_out("setting the path to [" + _streampath.string() + "]", obj); 00465 } 00466 else 00467 { 00468 // if the object on the top of the stack is a composite, then add this object to that. 00469 if (!test_composition(_stack.top())) 00470 return; 00471 00472 // create the object. 00473 obj = create_object(_stack.top(), element, val); 00474 00475 // object could not be created for some reason. 00476 if (!obj) 00477 { 00478 error(efmt_fail_create, element, val, true); 00479 _abort = true; 00480 return; 00481 } 00482 00483 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00484 { 00485 obj_out("composite (3)", _stack.top()); 00486 obj_out("adding to composite (3)", obj); 00487 } 00488 00489 if (!_stack.top()->composition()->composite()->add(obj, true)) 00490 { 00491 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true); 00492 _abort = true; 00493 return; 00494 } 00495 } 00496 00497 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00498 obj_out("pushing obj with name", obj); 00499 _stack.push(obj); 00500 } 00501 else if (_stack.empty()) 00502 { 00503 error(efmt_bad_stack, true); 00504 _abort = true; 00505 } 00506 else 00507 { 00508 error(efmt_bad_tag, element, true); 00509 _abort = true; 00510 } 00511 } 00512 } 00513 } 00514 00515 void xml::parseobj::cdata_handler(const xmlstring &s, int len) 00516 { 00517 if (_abort) 00518 return; 00519 00520 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00521 *_errorhandler << ">> cdata : [" << s << "]" << std::endl; 00522 00523 _cdata += s; 00524 } 00525 00526 void xml::parseobj::comment_handler(const xmlstring &s) 00527 { 00528 if (_abort) 00529 return; 00530 00531 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00532 *_errorhandler << ">> comment : [" << s << "]" << std::endl; 00533 00534 _comment = s; 00535 } 00536 00537 void xml::parseobj::endelement_handler(const xmlstring &element) 00538 { 00539 if (_abort) 00540 return; 00541 00542 if (_debug & (PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00543 *_errorhandler << ">> endelement : [" << element << "]" << std::endl; 00544 00545 if (_stack.empty()) 00546 { 00547 error(efmt_bad_stack, true); 00548 _abort = true; 00549 return; 00550 } 00551 00552 if (element == S("import") || element == S("xi:include")) 00553 { 00554 if (_context->delayed_import()) 00555 { 00556 // add proxy object as the object. 00557 if (!_delayed_obj) 00558 { 00559 error(efmt_bad_endimport, element, true); 00560 _abort = true; 00561 return; 00562 } 00563 ph::common::object_base *obj = _stack.top(); 00564 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00565 obj_out("popping because delayed", obj); 00566 // if we are closing off a delayed, just pop it off. 00567 _stack.pop(); 00568 _delayed_obj = 0; 00569 } 00570 else 00571 { 00572 if (!_import) 00573 { 00574 error(efmt_bad_endimport, element, true); 00575 _abort = true; 00576 return; 00577 } 00578 00579 if (!parse::parse_xml(_importstream, _importstreampath.string(), _import, 0)) 00580 { 00581 error(efmt_bad_urlstream, element, true); 00582 _abort = true; 00583 return; 00584 } 00585 00586 ph::common::object_base *obj = _import->obj(); 00587 if (!obj) 00588 { 00589 error(efmt_bad_import, _importstreampath.string(), true); 00590 _abort = true; 00591 return; 00592 } 00593 00594 // make sure that the object get's the correct name. 00595 if (!_import->_rootname.empty()) 00596 set_persistable_obj_name(obj, _import->_rootname); 00597 00598 // set the filepath for the object. 00599 if (!test_persistable(obj)) 00600 return; 00601 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00602 obj_out("setting the path to [" + _importstreampath.string() + "]", obj); 00603 // TBD: the following should be a real path. 00604 obj->persistable()->set_file_path(_importstreampath.string()); 00605 00606 // if the object on the top of the stack is a composite, then add this object to that. 00607 if (!test_composition(_stack.top())) 00608 return; 00609 00610 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00611 { 00612 obj_out("composite (4)", _stack.top()); 00613 obj_out("adding to composite (4)", obj); 00614 } 00615 00616 if (!_stack.top()->composition()->composite()->add(obj, true)) 00617 { 00618 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true); 00619 _abort = true; 00620 return; 00621 } 00622 00623 // finish up. 00624 delete _importstream; 00625 _importstream = 0; 00626 delete _import; 00627 _import = 0; 00628 } 00629 } 00630 else if (element == "param") 00631 { 00632 // end of parameters. 00633 } 00634 else if (element == "pathparam") 00635 { 00636 // end of path parameters. 00637 } 00638 else if (element == "var") 00639 { 00640 // end of variable. 00641 } 00642 else if (element == "pathvar") 00643 { 00644 // end of path variable. 00645 } 00646 else if (element == S("fragment")) 00647 { 00648 if (!_fragment) 00649 { 00650 error(efmt_bad_endfragment, element, true); 00651 _abort = true; 00652 return; 00653 } 00654 00655 // pop the stack. 00656 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00657 obj_out("popping", _stack.top()); 00658 _stack.pop(); 00659 _fragment = false; 00660 } 00661 else if (_inmember) 00662 { 00663 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00664 obj_out("setting member [" + element + "] of ", _stack.top()); 00665 if (!test_persistable(_stack.top())) 00666 return; 00667 00668 try { 00669 _stack.top()->persistable()->set(element, dosubs(_cdata)); 00670 } 00671 catch (...) 00672 { 00673 error(efmt_bad_data, element, _cdata, true); 00674 _abort = true; 00675 return; 00676 } 00677 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00678 *_errorhandler << ">> emptying cdata." << std::endl; 00679 _cdata = ""; 00680 _inmember = false; 00681 } 00682 else 00683 { 00684 ph::common::object_base *obj = _stack.top(); 00685 if (obj_type(obj) == element) 00686 { 00687 // see if _cdata is anything other than whitespace. 00688 if (_cdata.find_first_not_of(" \t\n") != std::string::npos) 00689 *_errorhandler << ">> Ignoring cdata. [" << _cdata << "]" << std::endl; 00690 00691 // closing out the current object 00692 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00693 obj_out("closing out ", obj); 00694 _stack.pop(); 00695 00696 if (_stack.empty()) 00697 { 00698 // we are at the end. Pickup the object. 00699 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00700 obj_out("stashing as _obj", obj); 00701 _obj = obj; 00702 } 00703 } 00704 else if (obj->composition() && obj->composition()->composite()) 00705 { 00706 if (obj_name(obj) != element) 00707 { 00708 error(efmt_bad_tag, element, true); 00709 _abort = true; 00710 return; 00711 } 00712 00713 if (_cdata.find_first_not_of(" \t\n") != std::string::npos) 00714 { 00715 if (obj->composition()->composite()->singleton() 00716 && obj->composition()->composite()->count() == 0) 00717 { 00718 // find the object that the cdata refers to, and add that as a reference. 00719 ph::common::object_base *o = _context->find_object(_root, obj, _cdata); 00720 if (!o) 00721 { 00722 error(efmt_bad_ref_location, _cdata, true); 00723 _abort = true; 00724 return; 00725 } 00726 00727 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00728 { 00729 obj_out("composite (5)", obj); 00730 obj_out("adding to composite (5)", o); 00731 } 00732 00733 // add the object to the composite, but it's not owned. 00734 if (!obj->composition()->composite()->add(o, false)) 00735 { 00736 error(efmt_bad_aggr_obj, obj_name(obj), obj_type(obj), true); 00737 _abort = true; 00738 return; 00739 } 00740 00741 // we have used up cdata. 00742 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00743 *_errorhandler << ">> emptying cdata." << std::endl; 00744 _cdata = ""; 00745 00746 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00747 obj_out("popping because composite", obj); 00748 // if we are closing off a composite, just pop it off. 00749 _stack.pop(); 00750 } 00751 else 00752 { 00753 // we can help generic out at this point. The only way that it knows if 00754 // an element is a composite or if it's a member is if it get's some 00755 // type of non-whitespace CDATA through. We call set as if this 00756 // is a member. This might cause some concern for other parsers since they'll 00757 // see members AND composites, but they can be written the same. 00758 00759 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00760 obj_out("popping because composite (possible member?)", obj); 00761 // if we are closing off a composite, just pop it off. 00762 _stack.pop(); 00763 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00764 obj_out("setting member [" + element + "] of ", _stack.top()); 00765 if (!test_persistable(_stack.top())) 00766 return; 00767 try 00768 { 00769 _stack.top()->persistable()->set(element, dosubs(_cdata)); 00770 } 00771 catch (...) 00772 { 00773 error(efmt_bad_data, element, _cdata, true); 00774 _abort = true; 00775 return; 00776 } 00777 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00778 *_errorhandler << ">> emptying cdata." << std::endl; 00779 _cdata = ""; 00780 } 00781 } 00782 else 00783 { 00784 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG)) 00785 obj_out("popping because composite", obj); 00786 // if we are closing off a composite, just pop it off. 00787 _stack.pop(); 00788 } 00789 } 00790 else 00791 { 00792 error(efmt_bad_tag, element, true); 00793 _abort = true; 00794 } 00795 } 00796 } 00797 00798 ph::common::object_base *xml::parseobj::create_object(ph::common::object_base *top, const xmlstring &type, const xmlstring &name) 00799 { 00800 // see if the tos is a composite. 00801 // if it's a composite, then we need to use it's parent 00802 // to clone actual objects off. 00803 ph::common::object_base *topsearch = top; 00804 if (top->composition() && top->composition()->composite()) 00805 { 00806 if (!test_outerable(top)) 00807 return 0; 00808 topsearch = top->outerable()->outer(); 00809 } 00810 00811 if (_debug & PARSEOBJ_OBJECT_DEBUG) 00812 *_errorhandler << ">> create_object type: [" << type 00813 << "] name: [" << name 00814 << "] top->name: [" << obj_name(top) << "]" << std::endl; 00815 00816 // try to create the new object using the top object. 00817 if (!test_persistable(top)) 00818 return 0; 00819 ph::common::object_base *obj = topsearch->persistable()->create(type, name, _context); 00820 00821 // if the above fails, ask the object manager. 00822 if (!obj) 00823 obj = _context->create(type, name); 00824 00825 if (obj) 00826 { 00827 // make sure we set the parent straight away so that templates etc 00828 // are used correctly. 00829 if (obj->outerable()) 00830 obj->outerable()->outer(top); 00831 } 00832 00833 return obj; 00834 } 00835 00836 xmlstring xml::parseobj::dosubs(const xmlstring &s) 00837 /* 00838 Substitute string values in the string. 00839 00840 All $(arg) pairs are substitute. 00841 */ 00842 { 00843 xmlstring remain = s; 00844 xmlstring news = S(""); 00845 int loc = remain.find(S("$(")); 00846 if (loc >= 0) 00847 { 00848 while (loc >= 0) 00849 { 00850 news += remain.substr(0, loc); 00851 remain = remain.substr(loc); 00852 loc = remain.find(L')'); 00853 if (loc >= 0) 00854 { 00855 xmlstring token = remain.substr(2, loc-2); 00856 remain = remain.substr(loc+1); 00857 00858 std::string value; 00859 if (find_param(token, &value)) 00860 news += value; 00861 else 00862 news += S("$(") + token + S(")"); 00863 } 00864 loc = remain.find(S("$(")); 00865 } 00866 news += remain; 00867 } 00868 00869 if (news.empty()) 00870 return s; 00871 else 00872 return news; 00873 } 00874 00875 void xml::parseobj::add_params(const std::map<std::string, std::string> ¶ms) 00876 { 00877 // copy the map. This code could be much simpler I think... 00878 std::for_each(params.begin(), params.end(), 00879 boost::bind(&add_param_1, 00880 &_params, 00881 boost::bind(&std::map<std::string, std::string>::value_type::first, _1), 00882 boost::bind(&std::map<std::string, std::string>::value_type::second, _1))); 00883 } 00884 00885 void xml::parseobj::add_param_1(std::map<std::string, std::string> *params, 00886 const std::string &name, const std::string &val) 00887 { 00888 (*params)[name] = val; 00889 } 00890 00891 void xml::parseobj::add_param(const std::string &name, const std::string &val) 00892 { 00893 add_param_1(&_params, name, val); 00894 } 00895 00896 bool xml::parseobj::find_param(const std::string &name, std::string *value) 00897 { 00898 std::map<std::string, std::string>::iterator i = _params.find(name); 00899 if (i == _params.end()) 00900 return false; 00901 *value = i->second; 00902 return true; 00903 } 00904 00905 bool xml::parseobj::test_outerable(ph::common::object_base *obj) 00906 { 00907 if (!obj->outerable()) 00908 { 00909 error(efmt_no_interface, "outerable", obj_name(obj), true); 00910 _abort = true; 00911 return false; 00912 } 00913 return true; 00914 } 00915 00916 bool xml::parseobj::test_persistable(ph::common::object_base *obj) 00917 { 00918 if (!obj->persistable()) 00919 { 00920 error(efmt_no_interface, "persistable", obj_name(obj), true); 00921 _abort = true; 00922 return false; 00923 } 00924 return true; 00925 } 00926 00927 bool xml::parseobj::test_composition(ph::common::object_base *obj) 00928 { 00929 if (!obj->composition()) 00930 { 00931 error(efmt_no_interface, "composition", obj_name(obj), true); 00932 _abort = true; 00933 return false; 00934 } 00935 if (!obj->composition()->composite()) 00936 { 00937 error(efmt_not_composite, obj_name(obj), true); 00938 _abort = true; 00939 return false; 00940 } 00941 return true; 00942 } 00943 00944 void xml::parseobj::obj_out(const std::string &msg, ph::common::object_base *obj) 00945 { 00946 *_errorhandler << ">> " << msg << " [" << obj_type(obj) << ", " << obj_name(obj) << "]" << std::endl; 00947 } 00948 00949 void xml::parseobj::element_out(const std::string &msg, const xmlstring &element) 00950 { 00951 *_errorhandler << ">> " << msg << " [" << element << "]" << std::endl; 00952 } 00953 00954 // TBD: These don't work. 00955 xmlstring xml::parseobj::getdecodedattrval(const std::vector<xmlstring> &atts, int index) 00956 { 00957 xmlstring val = attrval(atts, index); 00958 xmlstring news; 00959 if (decodexmldata(val, &news)) 00960 return news; 00961 else 00962 return val; 00963 } 00964 00965 xmlstring xml::parseobj::getdecodedexpectedattr(const std::vector<xmlstring> &atts, const xmlstring &token) 00966 { 00967 xmlstring val = expectedattr(atts, token); 00968 xmlstring news; 00969 if (decodexmldata(val, &news)) 00970 return news; 00971 else 00972 return val; 00973 } 00974 00975 xmlstring xml::parseobj::getdecodedattr(const std::vector<xmlstring> &atts, const xmlstring &token) 00976 { 00977 xmlstring val = attr(atts, token); 00978 xmlstring news; 00979 if (decodexmldata(val, &news)) 00980 return news; 00981 else 00982 return val; 00983 }