2012-05-03 21:44:52 +04:00
|
|
|
/*
|
|
|
|
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 "Node.hh"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
|
|
|
#include <functional>
|
2012-05-05 22:37:54 +04:00
|
|
|
#include <iterator>
|
2012-05-03 21:44:52 +04:00
|
|
|
#include <stdexcept>
|
|
|
|
|
2012-05-05 22:37:54 +04:00
|
|
|
// debugging
|
|
|
|
#include <iostream>
|
|
|
|
|
2012-05-03 21:44:52 +04:00
|
|
|
namespace gr { namespace xml {
|
|
|
|
|
|
|
|
class Node::Impl
|
|
|
|
{
|
|
|
|
private :
|
|
|
|
typedef std::vector<Impl*> ImplVec ;
|
|
|
|
|
|
|
|
public :
|
|
|
|
typedef ImplVec::iterator iterator ;
|
2012-05-06 07:50:37 +04:00
|
|
|
typedef ImplVec::const_iterator const_iterator ;
|
2012-05-03 21:44:52 +04:00
|
|
|
|
|
|
|
public :
|
2012-05-05 09:01:23 +04:00
|
|
|
Impl() : m_ref(1), m_type( element )
|
2012-05-03 21:44:52 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-05-05 11:00:23 +04:00
|
|
|
Impl( const std::string& str, Type type, const std::string& value = "" ) :
|
2012-05-03 21:44:52 +04:00
|
|
|
m_ref(1),
|
|
|
|
m_type( type ),
|
2012-05-05 11:00:23 +04:00
|
|
|
m_name( str ),
|
|
|
|
m_value( value )
|
2012-05-03 21:44:52 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~Impl()
|
|
|
|
{
|
|
|
|
std::for_each( m_children.begin(), m_children.end(), std::mem_fun( &Impl::Release ) ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Impl* AddRef()
|
|
|
|
{
|
|
|
|
++m_ref ;
|
|
|
|
return this ;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Release()
|
|
|
|
{
|
|
|
|
if ( --m_ref == 0 )
|
|
|
|
delete this ;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::size_t RefCount() const
|
|
|
|
{
|
|
|
|
assert( m_ref > 0 ) ;
|
|
|
|
return m_ref ;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Add( Impl *child )
|
|
|
|
{
|
|
|
|
assert( child != 0 ) ;
|
2012-05-05 11:00:23 +04:00
|
|
|
assert( child->m_type >= element && child->m_type <= text ) ;
|
2012-05-03 21:44:52 +04:00
|
|
|
|
2012-05-05 11:00:23 +04:00
|
|
|
ImplVec *map[] = { &m_element, &m_attr, 0 } ;
|
|
|
|
|
|
|
|
if ( map[child->m_type] != 0 )
|
2012-05-05 09:01:23 +04:00
|
|
|
{
|
2012-05-05 11:00:23 +04:00
|
|
|
ImplVec& vec = *map[child->m_type] ;
|
|
|
|
iterator p = std::lower_bound( vec.begin(), vec.end(), child, Comp() ) ;
|
|
|
|
|
2012-05-05 09:01:23 +04:00
|
|
|
// cannot allow duplicate attribute nodes
|
2012-05-05 11:00:23 +04:00
|
|
|
if ( child->m_type == attr && p != vec.end() &&
|
|
|
|
(*p)->m_type == attr && (*p)->m_name == child->m_name )
|
2012-05-05 09:01:23 +04:00
|
|
|
throw std::runtime_error( "duplicate attribute " + child->m_name ) ;
|
|
|
|
|
2012-05-05 11:00:23 +04:00
|
|
|
vec.insert( p, child ) ;
|
2012-05-05 09:01:23 +04:00
|
|
|
}
|
2012-05-03 21:44:52 +04:00
|
|
|
|
|
|
|
m_children.push_back( child ) ;
|
|
|
|
}
|
|
|
|
|
2012-05-05 09:01:23 +04:00
|
|
|
Impl* Find( const std::string& name )
|
2012-05-03 21:44:52 +04:00
|
|
|
{
|
2012-05-05 11:00:23 +04:00
|
|
|
assert( !name.empty() ) ;
|
|
|
|
|
|
|
|
return name[0] == '@'
|
|
|
|
? Find( m_attr, name.substr(1) )
|
|
|
|
: Find( m_element, name ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Impl* Find( ImplVec& map, const std::string& name )
|
|
|
|
{
|
|
|
|
Impl tmp( name , element ) ;
|
|
|
|
iterator i = std::lower_bound( map.begin(), map.end(), &tmp, Comp() ) ;
|
|
|
|
|
|
|
|
return i != map.end() && (*i)->m_name == name ? *i : 0 ;
|
2012-05-03 21:44:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
iterator Begin()
|
|
|
|
{
|
|
|
|
return m_children.begin() ;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator End()
|
|
|
|
{
|
|
|
|
return m_children.end() ;
|
|
|
|
}
|
|
|
|
|
2012-05-06 07:50:37 +04:00
|
|
|
const_iterator Begin() const
|
|
|
|
{
|
|
|
|
return m_children.begin() ;
|
|
|
|
}
|
|
|
|
|
|
|
|
const_iterator End() const
|
|
|
|
{
|
|
|
|
return m_children.end() ;
|
|
|
|
}
|
|
|
|
|
2012-05-05 09:01:23 +04:00
|
|
|
const std::string& Name() const
|
2012-05-03 21:44:52 +04:00
|
|
|
{
|
2012-05-05 09:01:23 +04:00
|
|
|
return m_name ;
|
2012-05-03 21:44:52 +04:00
|
|
|
}
|
|
|
|
|
2012-05-06 07:50:37 +04:00
|
|
|
std::string Value() const
|
2012-05-05 11:00:23 +04:00
|
|
|
{
|
2012-05-06 07:50:37 +04:00
|
|
|
std::string value = m_value ;
|
|
|
|
for ( const_iterator i = Begin() ; i != End() ; ++i )
|
|
|
|
value += (*i)->Value() ;
|
|
|
|
|
|
|
|
return value ;
|
2012-05-05 11:00:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Value( const std::string& val )
|
2012-05-03 21:44:52 +04:00
|
|
|
{
|
2012-05-05 11:00:23 +04:00
|
|
|
m_value = val ;
|
2012-05-03 21:44:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Type GetType() const
|
|
|
|
{
|
|
|
|
return m_type ;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Comp
|
|
|
|
{
|
|
|
|
bool operator()( Impl *p1, Impl *p2 ) const
|
|
|
|
{
|
2012-05-05 09:01:23 +04:00
|
|
|
return p1->Name() < p2->Name() ;
|
2012-05-03 21:44:52 +04:00
|
|
|
}
|
|
|
|
} ;
|
|
|
|
|
|
|
|
private :
|
|
|
|
std::size_t m_ref ;
|
|
|
|
|
|
|
|
Type m_type ;
|
2012-05-05 09:01:23 +04:00
|
|
|
std::string m_name ;
|
2012-05-05 11:00:23 +04:00
|
|
|
std::string m_value ;
|
|
|
|
ImplVec m_element, m_attr ;
|
2012-05-03 21:44:52 +04:00
|
|
|
ImplVec m_children ;
|
|
|
|
} ;
|
|
|
|
|
2012-05-06 19:16:43 +04:00
|
|
|
Node::iterator::iterator( ImplVec::iterator it ) : m_it( it )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::iterator::value_type Node::iterator::operator*() const
|
|
|
|
{
|
|
|
|
return Node( *m_it ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::iterator Node::iterator::operator++()
|
|
|
|
{
|
|
|
|
m_it++ ;
|
|
|
|
return *this ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::iterator Node::iterator::operator++(int)
|
|
|
|
{
|
|
|
|
iterator tmp( *this ) ;
|
|
|
|
++tmp ;
|
|
|
|
return tmp ;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Node::iterator::operator==( const iterator& i ) const
|
|
|
|
{
|
|
|
|
return m_it == i.m_it ;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Node::iterator::operator!=( const iterator& i ) const
|
|
|
|
{
|
|
|
|
return m_it != i.m_it ;
|
|
|
|
}
|
|
|
|
|
2012-05-03 21:44:52 +04:00
|
|
|
Node::Node() : m_ptr( new Impl )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::Node( const Node& node ) : m_ptr( node.m_ptr->AddRef() )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::Node( Impl *impl ) : m_ptr( impl )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Node Node::Element( const std::string& name )
|
|
|
|
{
|
|
|
|
return Node( new Impl( name, element ) ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node Node::Text( const std::string& name )
|
|
|
|
{
|
|
|
|
return Node( new Impl( name, text ) ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::~Node()
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
|
|
|
m_ptr->Release() ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node& Node::operator=( const Node& node )
|
|
|
|
{
|
|
|
|
Node tmp( node ) ;
|
|
|
|
std::swap( tmp.m_ptr, m_ptr ) ;
|
|
|
|
return *this ;
|
|
|
|
}
|
|
|
|
|
2012-05-05 09:01:23 +04:00
|
|
|
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] ;
|
|
|
|
}
|
|
|
|
|
2012-05-03 21:44:52 +04:00
|
|
|
Node Node::AddElement( const std::string& name )
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
2012-05-05 09:01:23 +04:00
|
|
|
assert( IsCompatible( GetType(), element) ) ;
|
2012-05-03 21:44:52 +04:00
|
|
|
|
|
|
|
Impl *child = new Impl( name, element ) ;
|
|
|
|
m_ptr->Add( child->AddRef() ) ;
|
|
|
|
return Node( child ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node Node::AddText( const std::string& str )
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
2012-05-05 09:01:23 +04:00
|
|
|
assert( IsCompatible( GetType(), text ) ) ;
|
2012-05-03 21:44:52 +04:00
|
|
|
|
2012-05-06 07:50:37 +04:00
|
|
|
Impl *child = new Impl( "#text", text, str ) ;
|
2012-05-03 21:44:52 +04:00
|
|
|
m_ptr->Add( child->AddRef() ) ;
|
|
|
|
return Node( child ) ;
|
|
|
|
}
|
|
|
|
|
2012-05-05 09:01:23 +04:00
|
|
|
void Node::AddAttribute( const std::string& name, const std::string& val )
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
|
|
|
assert( GetType() == element ) ;
|
2012-05-05 11:00:23 +04:00
|
|
|
m_ptr->Add( new Impl( name, attr, val ) ) ;
|
2012-05-05 09:01:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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() ) ;
|
|
|
|
}
|
|
|
|
|
2012-05-03 21:44:52 +04:00
|
|
|
Node Node::operator[]( const std::string& name ) const
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
2012-05-05 11:00:23 +04:00
|
|
|
assert( !name.empty() ) ;
|
|
|
|
|
2012-05-03 21:44:52 +04:00
|
|
|
Impl *i = m_ptr->Find( name ) ;
|
|
|
|
if ( i != 0 )
|
|
|
|
return Node( i->AddRef() ) ;
|
|
|
|
|
|
|
|
throw std::runtime_error( "node " + name + " can't be found" ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::size_t Node::RefCount() const
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
|
|
|
return m_ptr->RefCount() ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::Type Node::GetType() const
|
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
|
|
|
return m_ptr->GetType() ;
|
|
|
|
}
|
|
|
|
|
2012-05-05 09:01:23 +04:00
|
|
|
const std::string& Node::Name() const
|
2012-05-03 21:44:52 +04:00
|
|
|
{
|
|
|
|
assert( m_ptr != 0 ) ;
|
2012-05-05 09:01:23 +04:00
|
|
|
return m_ptr->Name() ;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Node::Value() const
|
|
|
|
{
|
2012-05-05 11:00:23 +04:00
|
|
|
assert( m_ptr != 0 ) ;
|
2012-05-06 07:50:37 +04:00
|
|
|
|
2012-05-05 11:00:23 +04:00
|
|
|
return m_ptr->Value() ;
|
2012-05-03 21:44:52 +04:00
|
|
|
}
|
|
|
|
|
2012-05-05 22:37:54 +04:00
|
|
|
std::vector<Node> Node::Children() const
|
|
|
|
{
|
|
|
|
std::vector<Node> result ;
|
|
|
|
for ( Impl::iterator i = m_ptr->Begin() ; i != m_ptr->End() ; ++i )
|
|
|
|
result.push_back( Node( (*i)->AddRef() ) ) ;
|
|
|
|
|
|
|
|
return result ;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream& operator<<( std::ostream& os, const Node& node )
|
|
|
|
{
|
2012-05-06 13:13:48 +04:00
|
|
|
if ( node.GetType() == Node::element )
|
|
|
|
{
|
|
|
|
os << '<' << node.Name() << ' ' ;
|
|
|
|
}
|
2012-05-05 22:37:54 +04:00
|
|
|
|
|
|
|
std::vector<Node> c = node.Children() ;
|
|
|
|
|
|
|
|
std::copy( c.begin(), c.end(), std::ostream_iterator<Node>(os, "\n") ) ;
|
|
|
|
return os ;
|
|
|
|
}
|
|
|
|
|
2012-05-06 19:16:43 +04:00
|
|
|
Node::iterator Node::begin()
|
|
|
|
{
|
|
|
|
return iterator( m_ptr->Begin() ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node::iterator Node::end()
|
|
|
|
{
|
|
|
|
return iterator( m_ptr->End() ) ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-03 21:44:52 +04:00
|
|
|
} } // end namespace
|