mirror of https://github.com/vitalif/grive2
added expat parser
parent
7d2b8b441d
commit
b12edc2a2a
|
@ -45,6 +45,7 @@ target_link_libraries( grive
|
|||
${CURL_LIBRARIES}
|
||||
${JSONC_LIBRARY}
|
||||
${OPENSSL_LIBRARIES}
|
||||
expat
|
||||
)
|
||||
|
||||
set_target_properties(grive PROPERTIES
|
||||
|
|
|
@ -35,14 +35,14 @@ public :
|
|||
typedef ImplVec::iterator iterator ;
|
||||
|
||||
public :
|
||||
Impl() : m_ref(1)
|
||||
Impl() : m_ref(1), m_type( element )
|
||||
{
|
||||
}
|
||||
|
||||
Impl( const std::string& str, Type type ) :
|
||||
m_ref(1),
|
||||
m_type( type ),
|
||||
m_str( str )
|
||||
m_name( str )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -74,16 +74,24 @@ public :
|
|||
assert( child != 0 ) ;
|
||||
|
||||
if ( child->m_type != text )
|
||||
m_map.insert( std::lower_bound( m_map.begin(), m_map.end(), child, Comp() ), child ) ;
|
||||
{
|
||||
iterator p = std::lower_bound( m_map.begin(), m_map.end(), child, Comp() ) ;
|
||||
|
||||
// cannot allow duplicate attribute nodes
|
||||
if ( child->m_type == attr && p != m_map.end() && (*p)->m_name == child->m_name )
|
||||
throw std::runtime_error( "duplicate attribute " + child->m_name ) ;
|
||||
|
||||
m_map.insert( p, child ) ;
|
||||
}
|
||||
|
||||
m_children.push_back( child ) ;
|
||||
}
|
||||
|
||||
Impl* Find( const std::string& str )
|
||||
Impl* Find( const std::string& name )
|
||||
{
|
||||
Impl tmp( str, element ) ;
|
||||
Impl tmp( name, element ) ;
|
||||
iterator i = std::lower_bound( m_map.begin(), m_map.end(), &tmp, Comp() ) ;
|
||||
return i != m_map.end() && (*i)->m_str == str ? *i : 0 ;
|
||||
return i != m_map.end() && (*i)->m_name == name ? *i : 0 ;
|
||||
}
|
||||
|
||||
iterator Begin()
|
||||
|
@ -96,14 +104,14 @@ public :
|
|||
return m_children.end() ;
|
||||
}
|
||||
|
||||
const std::string& Str() const
|
||||
const std::string& Name() const
|
||||
{
|
||||
return m_str ;
|
||||
return m_name ;
|
||||
}
|
||||
|
||||
void Str( const std::string& str )
|
||||
void Name( const std::string& name )
|
||||
{
|
||||
m_str = str ;
|
||||
m_name = name ;
|
||||
}
|
||||
|
||||
Type GetType() const
|
||||
|
@ -115,7 +123,7 @@ public :
|
|||
{
|
||||
bool operator()( Impl *p1, Impl *p2 ) const
|
||||
{
|
||||
return p1->Str() < p2->Str() ;
|
||||
return p1->Name() < p2->Name() ;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -123,7 +131,7 @@ private :
|
|||
std::size_t m_ref ;
|
||||
|
||||
Type m_type ;
|
||||
std::string m_str ;
|
||||
std::string m_name ;
|
||||
ImplVec m_map ;
|
||||
ImplVec m_children ;
|
||||
} ;
|
||||
|
@ -163,9 +171,25 @@ Node& Node::operator=( const Node& node )
|
|||
return *this ;
|
||||
}
|
||||
|
||||
bool Node::IsCompatible( Type parent, Type child )
|
||||
{
|
||||
static const bool map[][3] =
|
||||
{
|
||||
// element, attr, text
|
||||
{ true, true, true }, // element
|
||||
{ false, false, true }, // attribute
|
||||
{ false, false, false } // text
|
||||
} ;
|
||||
|
||||
assert( parent >= element && parent <= text ) ;
|
||||
assert( child >= element && child <= text ) ;
|
||||
return map[parent][child] ;
|
||||
}
|
||||
|
||||
Node Node::AddElement( const std::string& name )
|
||||
{
|
||||
assert( m_ptr != 0 ) ;
|
||||
assert( IsCompatible( GetType(), element) ) ;
|
||||
|
||||
Impl *child = new Impl( name, element ) ;
|
||||
m_ptr->Add( child->AddRef() ) ;
|
||||
|
@ -175,12 +199,33 @@ Node Node::AddElement( const std::string& name )
|
|||
Node Node::AddText( const std::string& str )
|
||||
{
|
||||
assert( m_ptr != 0 ) ;
|
||||
assert( IsCompatible( GetType(), text ) ) ;
|
||||
|
||||
Impl *child = new Impl( str, text ) ;
|
||||
m_ptr->Add( child->AddRef() ) ;
|
||||
return Node( child ) ;
|
||||
}
|
||||
|
||||
void Node::AddAttribute( const std::string& name, const std::string& val )
|
||||
{
|
||||
assert( m_ptr != 0 ) ;
|
||||
assert( GetType() == element ) ;
|
||||
|
||||
Impl *anode = new Impl( name, attr ) ;
|
||||
Impl *vnode = new Impl( val, text ) ;
|
||||
anode->Add( vnode ) ;
|
||||
m_ptr->Add( anode ) ;
|
||||
}
|
||||
|
||||
void Node::AddNode( const Node& node )
|
||||
{
|
||||
assert( m_ptr != 0 ) ;
|
||||
assert( node.m_ptr != 0 ) ;
|
||||
assert( IsCompatible( GetType(), node.GetType() ) ) ;
|
||||
|
||||
m_ptr->Add( node.m_ptr->AddRef() ) ;
|
||||
}
|
||||
|
||||
Node Node::operator[]( const std::string& name ) const
|
||||
{
|
||||
assert( m_ptr != 0 ) ;
|
||||
|
@ -203,10 +248,15 @@ Node::Type Node::GetType() const
|
|||
return m_ptr->GetType() ;
|
||||
}
|
||||
|
||||
const std::string& Node::Str() const
|
||||
const std::string& Node::Name() const
|
||||
{
|
||||
assert( m_ptr != 0 ) ;
|
||||
return m_ptr->Str() ;
|
||||
return m_ptr->Name() ;
|
||||
}
|
||||
|
||||
std::string Node::Value() const
|
||||
{
|
||||
return std::string() ;
|
||||
}
|
||||
|
||||
} } // end namespace
|
||||
|
|
|
@ -38,15 +38,21 @@ public :
|
|||
|
||||
Node AddElement( const std::string& name ) ;
|
||||
Node AddText( const std::string& text ) ;
|
||||
void AddNode( const Node& node ) ;
|
||||
void AddAttribute( const std::string& name, const std::string& val ) ;
|
||||
|
||||
Node operator[]( const std::string& name ) const ;
|
||||
const std::string& Str() const ;
|
||||
|
||||
const std::string& Name() const ;
|
||||
std::string Value() const ;
|
||||
|
||||
// read-only access to the reference counter. for checking.
|
||||
std::size_t RefCount() const ;
|
||||
|
||||
enum Type { element, attr, text } ;
|
||||
Type GetType() const ;
|
||||
|
||||
static bool IsCompatible( Type parent, Type child ) ;
|
||||
|
||||
private :
|
||||
class Impl ;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
grive: an GPL program to sync a local directory with Google Drive
|
||||
Copyright (C) 2012 Wan Wai Ho
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation version 2
|
||||
of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "TreeBuilder.hh"
|
||||
#include "Node.hh"
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
|
||||
namespace gr { namespace xml {
|
||||
|
||||
TreeBuilder::TreeBuilder() : m_stack( 1, Node() )
|
||||
{
|
||||
}
|
||||
|
||||
Node TreeBuilder::ParseFile( const std::string& file )
|
||||
{
|
||||
::XML_Parser p = ::XML_ParserCreate( 0 ) ;
|
||||
::XML_SetElementHandler( p, &TreeBuilder::StartElement, &TreeBuilder::EndElement ) ;
|
||||
|
||||
TreeBuilder tb ;
|
||||
::XML_SetUserData( p, &tb ) ;
|
||||
|
||||
std::ifstream f( file.c_str() ) ;
|
||||
|
||||
const std::size_t block_size = 10 ;
|
||||
std::size_t count = 0 ;
|
||||
while ( (count = f.rdbuf()->sgetn( (char*)::XML_GetBuffer( p, block_size ), block_size ) ) > 0 )
|
||||
XML_ParseBuffer( p, count, false ) ;
|
||||
XML_ParseBuffer( p, 0, true ) ;
|
||||
|
||||
assert( tb.m_stack.size() == 1 ) ;
|
||||
return tb.m_stack.front() ;
|
||||
}
|
||||
|
||||
Node TreeBuilder::Parse( const std::string& xml )
|
||||
{
|
||||
::XML_Parser p = ::XML_ParserCreate( 0 ) ;
|
||||
::XML_SetElementHandler( p, &TreeBuilder::StartElement, &TreeBuilder::EndElement ) ;
|
||||
|
||||
TreeBuilder tb ;
|
||||
::XML_SetUserData( p, &tb ) ;
|
||||
|
||||
XML_Parse( p, xml.c_str(), xml.size(), true ) ;
|
||||
|
||||
assert( tb.m_stack.size() == 1 ) ;
|
||||
return tb.m_stack.front() ;
|
||||
}
|
||||
|
||||
void TreeBuilder::StartElement( void *pvthis, const char *name, const char **attr )
|
||||
{
|
||||
assert( pvthis != 0 ) ;
|
||||
assert( name != 0 ) ;
|
||||
assert( attr != 0 ) ;
|
||||
|
||||
TreeBuilder *pthis = reinterpret_cast<TreeBuilder*>(pvthis) ;
|
||||
|
||||
Node n = pthis->m_stack.back().AddElement( name ) ;
|
||||
|
||||
for ( std::size_t i = 0 ; attr[i] != 0 ; i += 2 )
|
||||
{
|
||||
assert( attr[i+1] != 0 ) ;
|
||||
n.AddAttribute( attr[i], attr[i+1] ) ;
|
||||
}
|
||||
|
||||
pthis->m_stack.push_back( n ) ;
|
||||
}
|
||||
|
||||
void TreeBuilder::EndElement( void* pvthis, const char* name )
|
||||
{
|
||||
TreeBuilder *pthis = reinterpret_cast<TreeBuilder*>(pvthis) ;
|
||||
|
||||
assert( pthis->m_stack.back().Name() == name ) ;
|
||||
pthis->m_stack.pop_back() ;
|
||||
}
|
||||
|
||||
} } // end of namespace
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
grive: an GPL program to sync a local directory with Google Drive
|
||||
Copyright (C) 2012 Wan Wai Ho
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation version 2
|
||||
of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace gr { namespace xml {
|
||||
|
||||
class Node ;
|
||||
|
||||
class TreeBuilder
|
||||
{
|
||||
public :
|
||||
static Node ParseFile( const std::string& file ) ;
|
||||
static Node Parse( const std::string& xml ) ;
|
||||
|
||||
private :
|
||||
TreeBuilder() ;
|
||||
|
||||
static void StartElement( void* pvthis, const char* name, const char** attr ) ;
|
||||
static void EndElement( void* pvthis, const char* name ) ;
|
||||
|
||||
private :
|
||||
std::vector<Node> m_stack ;
|
||||
} ;
|
||||
|
||||
} } // end of namespace
|
|
@ -20,6 +20,7 @@
|
|||
#include "NodeTest.hh"
|
||||
|
||||
#include "xml/Node.hh"
|
||||
#include "xml/TreeBuilder.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
@ -49,7 +50,14 @@ void NodeTest::TestTree( )
|
|||
Node c11_ = node["child1"]["b"] ;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL( 3UL, c1_.RefCount() ) ;
|
||||
CPPUNIT_ASSERT_EQUAL( std::string("child1"), c1_.Str() ) ;
|
||||
CPPUNIT_ASSERT_EQUAL( std::string("child1"), c1_.Name() ) ;
|
||||
}
|
||||
|
||||
void NodeTest::TestParseFile( )
|
||||
{
|
||||
Node n = TreeBuilder::Parse( "<entry><link href=\"q\">abc</link></entry>" ) ;
|
||||
CPPUNIT_ASSERT_EQUAL( std::string("entry"), n["entry"].Name() ) ;
|
||||
CPPUNIT_ASSERT_EQUAL( std::string("q"), n["entry"]["link"].Name() ) ;
|
||||
}
|
||||
|
||||
} // end of namespace grut
|
||||
|
|
|
@ -32,10 +32,12 @@ public :
|
|||
// declare suit function
|
||||
CPPUNIT_TEST_SUITE( NodeTest ) ;
|
||||
CPPUNIT_TEST( TestTree ) ;
|
||||
CPPUNIT_TEST( TestParseFile ) ;
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
private :
|
||||
void TestTree( ) ;
|
||||
void TestParseFile( ) ;
|
||||
} ;
|
||||
|
||||
} // end of namespace
|
||||
|
|
Loading…
Reference in New Issue