OpenWalnut  1.4.0
WStructuredTextParser.cpp
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #include <algorithm>
00026 #include <sstream>
00027 #include <string>
00028 #include <vector>
00029 
00030 #include "exceptions/WParseError.h"
00031 #include "exceptions/WNotFound.h"
00032 #include "WIOTools.h"
00033 #include "WPredicateHelper.h"
00034 
00035 #include "WStructuredTextParser.h"
00036 
00037 namespace WStructuredTextParser
00038 {
00039     const std::string StructuredValueTree::Separator = "/";
00040 
00041     StructuredValueTree::StructuredValueTree( const FileType& file ):
00042         m_file( file )
00043     {
00044         // initialize member
00045     }
00046 
00047     StructuredValueTree::StructuredValueTree()
00048     {
00049         // do nothing.
00050     }
00051 
00052     StructuredValueTree::StructuredValueTree( const std::string& toParse ):
00053         m_file( parseFromString( toParse ) )
00054     {
00055         // initialize
00056     }
00057 
00058     StructuredValueTree::StructuredValueTree( const boost::filesystem::path& file ):
00059         m_file( parseFromFile( file ) )
00060     {
00061     }
00062 
00063     StructuredValueTree::~StructuredValueTree()
00064     {
00065         // cleanup
00066     }
00067 
00068     bool StructuredValueTree::exists( std::string key, bool valuesOnly ) const
00069     {
00070         return count( key, valuesOnly );
00071     }
00072 
00073     size_t StructuredValueTree::count( std::string key, bool valuesOnly ) const
00074     {
00075         std::vector< ObjectType > rObj;
00076         std::vector< KeyValueType > rKV;
00077 
00078         // traverse
00079         traverse( m_file, key, rObj, rKV );
00080 
00081         if( valuesOnly )
00082         {
00083             return rKV.size();
00084         }
00085         else
00086         {
00087             return rKV.size() + rObj.size();
00088         }
00089     }
00090 
00091     StructuredValueTree StructuredValueTree::getSubTree( std::string key ) const
00092     {
00093         std::vector< StructuredValueTree > r = getSubTrees( key );
00094 
00095         // return first match if any
00096         if( r.size() )
00097         {
00098             return *r.begin();
00099         }
00100         else
00101         {
00102             return StructuredValueTree();
00103         }
00104     }
00105 
00106     std::vector< StructuredValueTree > StructuredValueTree::getSubTrees( std::string key ) const
00107     {
00108         std::vector< ObjectType > rObj;
00109         std::vector< KeyValueType > rKV;
00110         std::vector< StructuredValueTree > r;
00111 
00112         // traverse
00113         traverse( m_file, key, rObj, rKV );
00114 
00115         // now we transform each found object int a MemberType (boost variant).
00116         for( std::vector< ObjectType >::const_iterator objects = rObj.begin(); objects != rObj.end(); ++objects )
00117         {
00118             // create a new StructuredValueTree instance
00119             r.push_back( StructuredValueTree( ( *objects ).m_nodes ) );
00120         }
00121 
00122         return r;
00123     }
00124 
00125     void StructuredValueTree::traverse( FileType current, std::string key,
00126                                                           std::vector< ObjectType >& resultObjects,
00127                                                           std::vector< KeyValueType >& resultValues ) const
00128     {
00129         // split up the key
00130         std::vector< std::string > keySplit = string_utils::tokenize( key, Separator, false );
00131         // empty key -> return empty result list
00132         if( !keySplit.size() )
00133         {
00134             return;
00135         }
00136 
00137         // traverse
00138         for( FileType::const_iterator i = current.begin(); i != current.end(); ++i )
00139         {
00140             traverse( *i, keySplit.begin(), keySplit.end(), resultObjects, resultValues );
00141         }
00142     }
00143 
00144     void StructuredValueTree::traverse( MemberType current, std::vector< std::string >::const_iterator keyIter,
00145                                                             std::vector< std::string >::const_iterator keyEnd,
00146                                                             std::vector< ObjectType >& resultObjects,
00147                                                             std::vector< KeyValueType >& resultValues ) const
00148     {
00149         // get some properties of the current entry:
00150         std::string elementName = boost::apply_visitor( NameQueryVisitor(), current );
00151         bool elementIsKeyValuePair = boost::apply_visitor( IsLeafVisitor(), current );
00152         bool elementIsComment = boost::apply_visitor( IsCommentVisitor(), current );
00153 
00154         // comments will be ignored.
00155         // NOTE: we store comments in the original data structure to allow them to be written again to the file. This can be useful if OW loads a
00156         // file (edited by the user) and re-writes this file. -> we are able to keep the comments
00157         if( elementIsComment )
00158         {
00159             return;
00160         }
00161 
00162         // does the current node match the current name?
00163         if( elementName == *keyIter )
00164         {
00165             // only if the key path continues AND the current element is no leaf, traverse
00166             if( !elementIsKeyValuePair && ( ( keyIter + 1 ) != keyEnd) )
00167             {
00168                 ObjectType elementAsObj = boost::get< ObjectType >( current );
00169                 for( std::vector< MemberType >::const_iterator nodeIter = elementAsObj.m_nodes.begin();
00170                     nodeIter != elementAsObj.m_nodes.end();
00171                     ++nodeIter )
00172                 {
00173                     traverse( *nodeIter, keyIter + 1, keyEnd, resultObjects, resultValues );
00174                 }
00175             }
00176             else if( ( keyIter + 1 ) == keyEnd )
00177             {
00178                 // we now have reached the end of the path.
00179                 if( elementIsKeyValuePair )
00180                 {
00181                     // the current element is a key-value pair -> add to result vector
00182                     resultValues.push_back( boost::get< KeyValueType >( current ) );
00183                 }
00184                 else
00185                 {
00186                     // the element is a object -> add to result vector
00187                     resultObjects.push_back( boost::get< ObjectType >( current ) );
00188                 }
00189             }
00190             // all the remaining cases are invalid and cause traversion to stop
00191         }
00192         // done
00193     }
00194 
00195     FileType parseFromString( std::string input )
00196     {
00197         std::ostringstream error;
00198         WStructuredTextParser::Grammar< std::string::const_iterator > parser( error );
00199 
00200         // parse
00201         FileType ast;
00202         std::string::const_iterator iter = input.begin();
00203         std::string::const_iterator end = input.end();
00204         bool r = phrase_parse( iter, end, parser, boost::spirit::ascii::space, ast );
00205 
00206         // error?
00207         if( !( r && iter == end ) )
00208         {
00209             throw WParseError( "Parse error. Parser message: " + error.str() );
00210         }
00211 
00212         // done. return
00213         return ast;
00214     }
00215 
00216     FileType parseFromFile( boost::filesystem::path path )
00217     {
00218         // NOTE: do not catch the io exception here.
00219         std::string input= readFileIntoString( path );
00220 
00221         // instantiate parser
00222         std::ostringstream error;
00223         WStructuredTextParser::Grammar< std::string::const_iterator > parser( error );
00224 
00225         // parse
00226         FileType ast;
00227         std::string::const_iterator iter = input.begin();
00228         std::string::const_iterator end = input.end();
00229         bool r = phrase_parse( iter, end, parser, boost::spirit::ascii::space, ast );
00230 
00231         // error?
00232         if( !( r && iter == end ) )
00233         {
00234             throw WParseError( "Parse error. Parser message: " + error.str() );
00235         }
00236 
00237         // done. return
00238         return ast;
00239     }
00240 }
00241