From 1e63ebe22b989523b96ccf97ee6769038b912e42 Mon Sep 17 00:00:00 2001 From: Chevtchenko Grigori Date: Fri, 1 Apr 2016 09:58:45 +0200 Subject: [PATCH] Improved parser and tests --- brion/target.cpp | 82 +++++++++++++++++++++++++++++++++----------------------- brion/target.h | 7 ++++- tests/target.cpp | 28 +++++++++++++++++++ 3 files changed, 83 insertions(+), 34 deletions(-) diff --git a/brion/target.cpp b/brion/target.cpp index 5e6c9c6..e1929a4 100644 --- a/brion/target.cpp +++ b/brion/target.cpp @@ -74,8 +74,14 @@ class Target const TargetType type = lexical_cast< TargetType >( typeStr ); _targetNames[type].push_back( name ); boost::trim( content ); - boost::split( _targetValues[name], content, boost::is_any_of("\n "), - boost::token_compress_on ); + if( content.empty( )) + _targetValues[name] = Strings(); + else + { + boost::split( _targetValues[name], content, + boost::is_any_of("\n "), + boost::token_compress_on ); + } } if( _targetNames.empty( )) @@ -91,12 +97,15 @@ class Target return empty; } + bool contains( const std::string& name ) const + { return _targetValues.find( name ) != _targetValues.end(); } + const Strings& get( const std::string& name ) const { ValueTable::const_iterator i = _targetValues.find( name ); if( i != _targetValues.end( )) return i->second; - LBTHROW( std::runtime_error( name + " not a valid target" )); + throw( std::runtime_error( name + " not a valid target" )); } private: @@ -137,49 +146,55 @@ const Strings& Target::getTargetNames( const TargetType type ) const return _impl->getTargetNames( type ); } +bool Target::contains( const std::string& name ) const +{ + return _impl->contains( name ); +} + const Strings& Target::get( const std::string& name ) const { return _impl->get( name ); } -namespace +GIDSet Target::parse( const Targets& targets, const std::string& root ) { -void _parse( const Targets& targets, - const std::string& name, - GIDSet& gids, - bool& found ) -{ - BOOST_FOREACH( const Target& target, targets ) + if( root.empty( )) + LBTHROW( std::runtime_error( "Empty target name" )); + + GIDSet gids; + Strings names( 1, root ); + while( !names.empty( )) { - try + const std::string name = names.back(); + names.pop_back(); + + if( name[0] == 'a' ) // maybe a GID { - const brion::Strings& values = target.get( name ); - found = true; - BOOST_FOREACH( const std::string& value, values ) + try { - try - { - gids.insert( lexical_cast< uint32_t >( value.substr( 1 ))); - } - catch( ... ) - { - if( value != name ) - _parse( targets, value, gids, found ); - } + gids.insert( lexical_cast< uint32_t >( name.substr( 1 ))); + continue; } + catch( const boost::bad_lexical_cast& ) {} // not a GID } - catch( ... ) {} + + bool found = false; + BOOST_FOREACH( const Target& target, targets ) + { + if( !target.contains( name )) + continue; + + const brion::Strings& values = target.get( name ); + std::copy( values.begin(), values.end(), + std::back_inserter( names )); + found = true; + break; + } + if( !found ) + LBTHROW( std::runtime_error( "Parse " + root + " failed: " + name + + " is not a valid target " )); } -} -} -GIDSet Target::parse( const Targets& targets, const std::string& name ) -{ - brion::GIDSet gids; - bool found = false; - _parse( targets, name, gids, found ); - if( !found ) - LBTHROW( std::runtime_error( name + " not a valid target" )); return gids; } @@ -200,3 +215,4 @@ std::ostream& operator << ( std::ostream& os, const Target& target ) } } + diff --git a/brion/target.h b/brion/target.h index aefee13..277f2ce 100644 --- a/brion/target.h +++ b/brion/target.h @@ -63,6 +63,9 @@ class Target */ BRION_API const Strings& getTargetNames( const TargetType type ) const; + /** @return true if the target exists. @version 1.7 */ + BRION_API bool contains( const std::string& name ) const; + /** Get targets and/or GIDSet grouped by the given target * * @param name target name to get the values from @@ -77,11 +80,13 @@ class Target * * All given targets are searched for the given name. If found, the named * target is recursively resolved to a GID set. + * Empty targets are valid, i.e., do not throw when an empty target is + * found. * * @param targets the targets to parse * @param name the target name to parse * @return the set of cell identifiers parsed - * @throw std::runtime_error if name is an invalid target + * @throw std::runtime_error if a non-existent (sub)target is found. * @version 1.6 */ BRION_API static GIDSet parse( const Targets& targets, diff --git a/tests/target.cpp b/tests/target.cpp index 647da36..a85af2c 100644 --- a/tests/target.cpp +++ b/tests/target.cpp @@ -79,3 +79,31 @@ BOOST_AUTO_TEST_CASE( parse ) brion::Targets( 1, target ), "Column" ); BOOST_CHECK_EQUAL( column.size(), 600 ); } + +BOOST_AUTO_TEST_CASE( parseBroken ) +{ + boost::filesystem::path pathUser( BBP_TEST_USER_TARGET ); + boost::filesystem::path pathStart( BBP_TEST_START_TARGET ); + + const brion::Target startTarget( pathUser.string( )); + const brion::Target userTarget( pathStart.string( )); + + brion::Targets testTargets; + testTargets.push_back( startTarget ); + testTargets.push_back( userTarget ); + + const brion::GIDSet& column = brion::Target::parse( + testTargets, "Column" ); + + BOOST_CHECK_NO_THROW( brion::Target::parse( + testTargets, "Column" )); + BOOST_CHECK_NO_THROW( brion::Target::parse( + testTargets, "EmptyColumn" )); + BOOST_CHECK_NO_THROW( brion::Target::parse( + testTargets, "EmptyTarget" )); + BOOST_CHECK_THROW( brion::Target::parse( + testTargets, "BrokenColumn" ), + std::runtime_error ); + + BOOST_CHECK_EQUAL( column.size(), 1000 ); +}