From: <br...@us...> - 2010-09-13 18:14:05
|
Revision: 4188 http://openvrml.svn.sourceforge.net/openvrml/?rev=4188&view=rev Author: braden Date: 2010-09-13 18:13:59 +0000 (Mon, 13 Sep 2010) Log Message: ----------- Add (partially) parsed nodes to the scene in the event of an exception during parsing; ensure that the browser::initialized event gets send and scene::scene_loaded gets called in the event of an exception during parsing. Modified Paths: -------------- trunk/ChangeLog trunk/src/libopenvrml/openvrml/browser.cpp trunk/src/libopenvrml/openvrml/local/parse_vrml.h trunk/src/libopenvrml/openvrml/scene.cpp Modified: trunk/ChangeLog =================================================================== --- trunk/ChangeLog 2010-08-20 19:26:31 UTC (rev 4187) +++ trunk/ChangeLog 2010-09-13 18:13:59 UTC (rev 4188) @@ -1,3 +1,24 @@ +2010-09-13 Braden McDaniel <br...@en...> + + Add (partially) parsed nodes to the scene in the event of an + exception during parsing; ensure that the browser::initialized + event gets send and scene::scene_loaded gets called in the event + of an exception during parsing. + + * src/libopenvrml/openvrml/local/parse_vrml.h + (openvrml::local::vrml97_parse_actions::~vrml97_parse_actions()): + Add (partially) parsed nodes to the scene if the parse_scope stack + is not empty (that is, parsing was terminated prior to the end of + correctly formed input). + * src/libopenvrml/openvrml/scene.cpp + (openvrml::scene::load(resource_istream &)): Ensure that + scene_loaded gets called in the event of an exception during + parsing. + * src/libopenvrml/openvrml/browser.cpp + (openvrml::browser::set_world(resource_istream &)): Ensure that + the browser::initialized event gets sent in the event of an + exception during parsing. + 2010-08-20 Braden McDaniel <br...@en...> * src/libopenvrml/openvrml/event.h Modified: trunk/src/libopenvrml/openvrml/browser.cpp =================================================================== --- trunk/src/libopenvrml/openvrml/browser.cpp 2010-08-20 19:26:31 UTC (rev 4187) +++ trunk/src/libopenvrml/openvrml/browser.cpp 2010-09-13 18:13:59 UTC (rev 4188) @@ -33,6 +33,7 @@ # include <boost/function.hpp> # include <boost/functional.hpp> # include <boost/lexical_cast.hpp> +# include <boost/scope_exit.hpp> # include <algorithm> # include <functional> # include <cerrno> @@ -1913,6 +1914,22 @@ using std::for_each; using boost::shared_lock; using boost::shared_mutex; + + // + // Ensure that the "initialized" event is sent even if parsing throws. + // + browser & self = *this; + shared_mutex & listeners_mutex = this->listeners_mutex_; + std::set<browser_listener *> & listeners = this->listeners_; + + BOOST_SCOPE_EXIT((&self)(&listeners_mutex)(&listeners)) { + shared_lock<shared_mutex> listeners_lock(listeners_mutex); + for_each(listeners.begin(), listeners.end(), + boost::bind2nd( + boost::mem_fun(&browser_listener::browser_changed), + browser_event(self, browser_event::initialized))); + } BOOST_SCOPE_EXIT_END + { using std::string; using boost::upgrade_lock; @@ -1993,11 +2010,6 @@ this->modified(true); this->new_view = true; // Force resetUserNav } // unlock this->scene_mutex_, this->active_viewpoint_mutex_ - - shared_lock<shared_mutex> listeners_lock(this->listeners_mutex_); - for_each(this->listeners_.begin(), this->listeners_.end(), - boost::bind2nd(boost::mem_fun(&browser_listener::browser_changed), - browser_event(*this, browser_event::initialized))); } /** Modified: trunk/src/libopenvrml/openvrml/local/parse_vrml.h =================================================================== --- trunk/src/libopenvrml/openvrml/local/parse_vrml.h 2010-08-20 19:26:31 UTC (rev 4187) +++ trunk/src/libopenvrml/openvrml/local/parse_vrml.h 2010-09-13 18:13:59 UTC (rev 4188) @@ -2,7 +2,7 @@ // // OpenVRML // -// Copyright 2009 Braden McDaniel +// Copyright 2009, 2010 Braden McDaniel // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by @@ -85,6 +85,75 @@ nodes_(nodes) {} + ~vrml97_parse_actions() + { + // + // If the parse scope stack is not empty, we must have hit a + // parse error. Since all inner scopes are associated with + // PROTOs, we'll just drop them. Back up to the top-level + // parse scope and use whatever nodes we have there. + // + if (!this->ps.empty()) { + while (this->ps.size() > 1) { this->ps.pop(); } + + parse_scope & ps = this->ps.top(); + + // + // When we start out, the children stack is one bigger + // than the node_data stack. The node_data stack doesn't + // get pushed until we start parsing a node; and it gets + // popped when we finish parsing a node. Once we start + // parsing a contained node, children gets pushed again + // and it's again one bigger than node_data. + // + // So, if the node_data and children stacks are the same + // size, that means we started parsing a node, but haven't + // gotten as far as parsing any contained nodes. In this + // case, just go ahead and call on_node_finish to create + // the "current" node and pop the node_data stack. + // + if (ps.node_data_.size() == ps.children.size()) { + this->on_node_finish(); + } + + try { + while (ps.children.size() > 1) { + assert(!ps.node_data_.empty()); + node_data & nd = ps.node_data_.top(); + assert(nd.current_field_value); + const field_value::type_id current_field_type = + nd.current_field_value->second->type(); + if (current_field_type == field_value::sfnode_id) { + nd.current_field_value->second->assign( + sfnode(ps.children.top().front())); + } else if (current_field_type + == field_value::mfnode_id) { + nd.current_field_value->second->assign( + mfnode(ps.children.top())); + } else { + assert(false); + } + ps.children.pop(); + + this->on_node_finish(); + } + + this->on_scene_finish(); + + } catch (std::exception & ex) { + // + // Known possible exceptions here are std::bad_alloc, + // std::bad_cast, and openvrml::unsupported_interface. + // We're in a destructor here, so we don't want to + // throw. + // + this->scene_.browser().err( + "caught exception while constructing scene from " + "invalid data: " + std::string(ex.what())); + } + } + } + struct on_scene_start_t { explicit on_scene_start_t(vrml97_parse_actions & actions): actions_(actions) @@ -144,6 +213,7 @@ add_route(*from, r->eventout, *to, r->eventin); } this->actions_.ps.pop(); + assert(this->actions_.ps.empty()); } private: @@ -760,7 +830,7 @@ .current_field_value->second->assign( sfnode(ps.children.top().front())); } - actions_.ps.top().children.pop(); + ps.children.pop(); } private: @@ -775,13 +845,13 @@ void operator()() const { assert(!this->actions_.ps.empty()); - assert(!this->actions_.ps.top().node_data_.empty()); - assert(!this->actions_.ps.top().children.empty()); - this->actions_.ps.top().node_data_.top() - .current_field_value->second - ->assign( - mfnode(this->actions_.ps.top().children.top())); - this->actions_.ps.top().children.pop(); + parse_scope & ps = this->actions_.ps.top(); + + assert(!ps.node_data_.empty()); + assert(!ps.children.empty()); + ps.node_data_.top().current_field_value->second + ->assign(mfnode(ps.children.top())); + ps.children.pop(); } private: @@ -1180,6 +1250,9 @@ // // We push a parse_scope onto the stack // * at the scene root + // * when starting a PROTO definition + // * when starting an SFNode or MFNode PROTO default value + // std::stack<parse_scope> ps; private: Modified: trunk/src/libopenvrml/openvrml/scene.cpp =================================================================== --- trunk/src/libopenvrml/openvrml/scene.cpp 2010-08-20 19:26:31 UTC (rev 4187) +++ trunk/src/libopenvrml/openvrml/scene.cpp 2010-09-13 18:13:59 UTC (rev 4188) @@ -3,7 +3,7 @@ // OpenVRML // // Copyright 1998 Chris Morley -// Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 Braden McDaniel +// Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2010 Braden McDaniel // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by @@ -25,6 +25,7 @@ # include <openvrml/local/parse_vrml.h> # include <private.h> # include <boost/function.hpp> +# include <boost/scope_exit.hpp> # ifdef HAVE_CONFIG_H # include <config.h> @@ -161,6 +162,9 @@ /** * @brief Load the @c scene from a stream. * + * This function calls @c #scene_loaded once parsing the scene from @p in has + * completed. + * * @param[in,out] in an input stream. * * @exception bad_media_type if @p in.type() is not @@ -171,6 +175,14 @@ */ void openvrml::scene::load(resource_istream & in) { + // + // Ensure that scene_loaded gets called even if parsing throws. + // + scene & self = *this; + BOOST_SCOPE_EXIT((&self)) { + self.scene_loaded(); + } BOOST_SCOPE_EXIT_END + { using boost::unique_lock; using boost::shared_mutex; @@ -185,7 +197,6 @@ local::parse_vrml(in, in.url(), in.type(), *this, this->nodes_, this->meta_); } - this->scene_loaded(); } /** This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |