load and save from .grive_state and build the resource tree

pull/40/head
Matchman Green 2012-05-20 17:12:01 +08:00
parent 3dd3f544fb
commit dfbe859531
18 changed files with 304 additions and 39 deletions

View File

@ -117,6 +117,7 @@ int main( int argc, char **argv )
Drive drive( token ) ;
drive.Update() ;
// drive.SaveState() ;
config.Save() ;
}

View File

@ -97,6 +97,7 @@ Drive::Drive( OAuth2& auth ) :
if ( file.Kind() != "folder" )
{
Resource *p = m_state.FindFolderByHref( file.ParentHref() ) ;
Trace( "finding parent of %1%: %2%", file.Title(), (void*)p ) ;
if ( file.Filename().empty() )
Log( "file \"%1%\" is a google document, ignored", file.Title() ) ;
@ -122,7 +123,10 @@ Drive::Drive( OAuth2& auth ) :
resp = xrsp.Response() ;
}
} while ( has_next ) ;
}
void Drive::SaveState()
{
m_state.Write( state_file ) ;
}

View File

@ -42,6 +42,7 @@ public :
void Update() ;
void Sync() ;
void SaveState() ;
struct Error : virtual Exception {} ;

View File

@ -56,6 +56,25 @@ Entry::Entry( const fs::path& path ) :
{
}
Entry::Entry(
const std::string& name,
const std::string& resource_id,
const std::string& href,
const std::string& md5,
const std::string& kind,
const DateTime& mtime,
const std::string& parent_href ) :
m_title ( name ),
m_filename ( name ),
m_kind ( kind ),
m_md5 ( md5 ),
m_resource_id ( resource_id ),
m_parent_hrefs ( 1, parent_href ),
m_self_href ( href ),
m_mtime ( mtime )
{
}
void Entry::Update( const xml::Node& n )
{
m_title = n["title"] ;

View File

@ -45,6 +45,14 @@ public :
Entry( ) ;
explicit Entry( const fs::path& path ) ;
explicit Entry( const xml::Node& n ) ;
explicit Entry(
const std::string& name,
const std::string& resource_id,
const std::string& href,
const std::string& md5,
const std::string& kind,
const DateTime& mtime,
const std::string& parent_href ) ;
std::string Title() const ;
std::string Filename() const ;
@ -78,7 +86,6 @@ private :
std::string m_self_href ;
std::string m_content_src ;
std::string m_parent_href ;
std::string m_upload_link ;
DateTime m_mtime ;

View File

@ -23,6 +23,7 @@
#include "http/Download.hh"
#include "http/StringResponse.hh"
#include "http/XmlResponse.hh"
#include "protocol/Json.hh"
#include "util/Crypt.hh"
#include "util/Log.hh"
#include "util/OS.hh"
@ -44,6 +45,21 @@ Resource::Resource() :
{
}
/// construct from previously serialized JSON object
Resource::Resource( const Json& json, Resource *parent ) :
m_entry (
json["name"].Str(),
json["id"].Str(),
json["href"].Str(),
json["md5"].Str(),
json["kind"].Str(),
DateTime( json["mtime"]["sec"].Int(), json["mtime"]["nsec"].Int() ),
parent != 0 ? parent->SelfHref() : "" ),
m_parent( parent ),
m_state( local_new )
{
}
Resource::Resource( const xml::Node& entry ) :
m_entry ( entry ),
m_parent( 0 ),
@ -58,16 +74,6 @@ Resource::Resource( const Entry& entry, Resource *parent ) :
{
}
// Resource::Resource(
// const std::string& name,
// const std::string& kind,
// const std::string& href ) :
// m_entry ( name, kind, href ),
// m_parent( 0 ),
// m_state ( local_new )
// {
// }
Resource::Resource( const fs::path& path ) :
m_entry ( path ),
m_parent( 0 ),
@ -127,7 +133,7 @@ std::string Resource::ParentHref() const
void Resource::AddChild( Resource *child )
{
assert( child != 0 ) ;
assert( child->m_parent == 0 ) ;
assert( child->m_parent == 0 || child->m_parent == this ) ;
assert( child != this ) ;
child->m_parent = this ;
@ -285,6 +291,46 @@ bool Resource::Upload( http::Agent* http, std::streambuf *file, const http::Head
return true ;
}
Json Resource::Serialize() const
{
Json result ;
result.Add( "name", Json(Name()) ) ;
result.Add( "id", Json(ResourceID()) ) ;
result.Add( "href", Json(SelfHref()) ) ;
result.Add( "md5", Json(m_entry.MD5()) ) ;
result.Add( "kind", Json(m_entry.Kind()) ) ;
Json mtime ;
mtime.Add( "sec", Json(m_entry.MTime().Sec() ) );
mtime.Add( "nsec", Json(m_entry.MTime().NanoSec() ) );
result.Add( "mtime",mtime ) ;
std::vector<Json> array ;
for ( std::vector<Resource*>::const_iterator i = m_child.begin() ; i != m_child.end() ; ++i )
array.push_back( (*i)->Serialize() ) ;
result.Add( "child", Json(array) ) ;
return result ;
}
Resource::iterator Resource::begin() const
{
return m_child.begin() ;
}
Resource::iterator Resource::end() const
{
return m_child.end() ;
}
std::size_t Resource::size() const
{
return m_child.size() ;
}
} // end of namespace
namespace std

View File

@ -30,6 +30,8 @@
namespace gr {
class Json ;
/*! \brief A resource can be a file or a folder in the google drive
The google drive contains a number of resources, which is represented by this class.
@ -40,15 +42,15 @@ class Resource
public :
struct Error : virtual Exception {} ;
typedef std::vector<Resource*> Children ;
typedef Children::const_iterator iterator ;
public :
Resource() ;
explicit Resource( const xml::Node& entry ) ;
explicit Resource( const Entry& entry, Resource *parent = 0 ) ;
explicit Resource( const fs::path& path ) ;
// Resource(
// const std::string& name,
// const std::string& kind,
// const std::string& href ) ;
explicit Resource( const Json& json, Resource *parent = 0 ) ;
void Swap( Resource& coll ) ;
// default copy ctor & op= are fine
@ -72,6 +74,13 @@ public :
void Sync( http::Agent *http, const http::Headers& auth ) ;
void Delete( http::Agent* http, const http::Headers& auth ) ;
Json Serialize() const ;
// children access
iterator begin() const ;
iterator end() const ;
std::size_t size() const ;
private :
/// State of the resource. indicating what to do with the resource
enum State

View File

@ -20,7 +20,9 @@
#include "ResourceTree.hh"
#include "CommonUri.hh"
#include "protocol/Json.hh"
#include "util/Destroy.hh"
#include "util/Log.hh"
#include <algorithm>
#include <cassert>
@ -52,10 +54,18 @@ ResourceTree::ResourceTree( const ResourceTree& fs ) :
}
ResourceTree::~ResourceTree( )
{
Clear() ;
}
void ResourceTree::Clear()
{
// delete all pointers
const Set& s = m_set.get<ByIdentity>() ;
std::for_each( s.begin(), s.end(), Destroy() ) ;
m_set.clear() ;
m_root = 0 ;
}
Resource* ResourceTree::Root()
@ -152,4 +162,30 @@ ResourceTree::iterator ResourceTree::end()
return m_set.get<ByIdentity>().end() ;
}
void ResourceTree::Read( const Json& json )
{
Clear() ;
m_root = new Resource( json ) ;
AddTree( m_root, json ) ;
}
void ResourceTree::AddTree( Resource *node, const Json& json )
{
assert( node != 0 ) ;
m_set.insert( node ) ;
std::vector<Json> array = json["child"].AsArray() ;
for ( std::vector<Json>::iterator i = array.begin() ; i != array.end() ; ++i )
{
Resource *c = new Resource( *i, node ) ;
node->AddChild( c ) ;
AddTree( c, *i ) ;
}
}
Json ResourceTree::Serialize() const
{
return m_root->Serialize() ;
}
} // end of namespace

View File

@ -30,6 +30,8 @@
namespace gr {
class Json ;
namespace details
{
using namespace boost::multi_index ;
@ -73,6 +75,7 @@ public :
const Resource* FindByHref( const std::string& href ) const ;
Resource* FindByPath( const fs::path& path ) ;
Resource* FindByID( const std::string& id ) ;
bool ReInsert( Resource *coll ) ;
@ -85,7 +88,14 @@ public :
iterator begin() ;
iterator end() ;
void Read( const Json& json ) ;
Json Serialize() const ;
private :
void Clear() ;
void AddTree( Resource *node, const Json& json ) ;
private :
details::Folders m_set ;
Resource* m_root ;

View File

@ -40,8 +40,7 @@ namespace gr {
State::State( const fs::path& filename )
{
if ( fs::exists( filename ) )
Read( filename );
Read( filename ) ;
}
std::string State::ChangeStamp() const
@ -68,15 +67,25 @@ void State::FromLocal( const fs::path& p, gr::Resource* folder )
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
{
if ( i->path().filename().string()[0] == '.' )
Log( "file %1% is ignored by grive", i->path().filename().string(), log::info ) ;
std::string fname = i->path().filename().string() ;
if ( fname[0] == '.' )
Log( "file %1% is ignored by grive", fname, log::info ) ;
else
{
Resource *c = new Resource( i->path() ) ;
folder->AddChild( c ) ;
m_res.Insert( c ) ;
Resource *c = folder->FindChild( fname ) ;
if ( c != 0 )
{
Trace( "wow! file %1% is loaded from previous state", fname ) ;
}
else
{
c = new Resource( i->path() ) ;
folder->AddChild( c ) ;
m_res.Insert( c ) ;
}
if ( fs::is_directory( i->path() ) )
FromLocal( *i, c ) ;
}
@ -124,7 +133,11 @@ bool State::Update( const Entry& e )
{
assert( !e.ParentHref().empty() ) ;
// Resource *r = m_res.FindByID( e.ResourceID() ) ;
Resource *r = m_res.FindByHref( e.SelfHref() ) ;
if ( r != 0 )
{
Trace( "wow! find %1% from state file?", r->Name() ) ;
}
Resource *parent = m_res.FindByHref( e.ParentHref() ) ;
if ( parent != 0 )
@ -183,13 +196,20 @@ State::iterator State::end()
void State::Read( const fs::path& filename )
{
Trace( "reading %1%", filename ) ;
if ( fs::exists( filename ) )
{
Json json = Json::ParseFile( filename.string() ) ;
m_change_stamp = json["change_stamp"].Str() ;
m_res.Read( json["rtree"] ) ;
}
}
void State::Write( const fs::path& filename ) const
{
Json result ;
result.Add( "change_stamp", Json( m_change_stamp ) ) ;
result.Add( "rtree", m_res.Serialize() ) ;
std::ofstream fs( filename.string().c_str() ) ;
fs << result ;

View File

@ -34,7 +34,7 @@ Json::Json( ) :
m_json( ::json_object_new_object() )
{
if ( m_json == 0 )
throw Error() << expt::ErrMsg( "cannot create json object" ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json object" ) ) ;
}
template <>
@ -42,7 +42,8 @@ Json::Json( const std::string& str ) :
m_json( ::json_object_new_string( str.c_str() ) )
{
if ( m_json == 0 )
throw Error() << expt::ErrMsg( "cannot create json string \"" + str + "\"" ) ;
BOOST_THROW_EXCEPTION(
Error() << expt::ErrMsg( "cannot create json string \"" + str + "\"" ) ) ;
// paranoid check
assert( ::json_object_get_string( m_json ) == str ) ;
@ -53,7 +54,7 @@ Json::Json( const int& l ) :
m_json( ::json_object_new_int( l ) )
{
if ( m_json == 0 )
throw Error() << expt::ErrMsg( "cannot create json int" ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json int" ) ) ;
}
template <>
@ -61,7 +62,15 @@ Json::Json( const long& l ) :
m_json( ::json_object_new_int( static_cast<int>(l) ) )
{
if ( m_json == 0 )
throw Error() << expt::ErrMsg( "cannot create json int" ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json int" ) ) ;
}
template <>
Json::Json( const unsigned long& l ) :
m_json( ::json_object_new_int( static_cast<int>(l) ) )
{
if ( m_json == 0 )
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json int" ) ) ;
}
template <>
@ -69,7 +78,7 @@ Json::Json( const std::vector<Json>& arr ) :
m_json( ::json_object_new_array( ) )
{
if ( m_json == 0 )
throw Error() << expt::ErrMsg( "cannot create json int" ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "cannot create json int" ) ) ;
for ( std::vector<Json>::const_iterator i = arr.begin() ; i != arr.end() ; ++i )
Add( *i ) ;
@ -79,7 +88,7 @@ Json Json::Parse( const std::string& str )
{
struct json_object *json = ::json_tokener_parse( str.c_str() ) ;
if ( json == 0 )
throw Error() << expt::ErrMsg( "json parse error" ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "json parse error" ) ) ;
return Json( json, NotOwned() ) ;
}
@ -98,7 +107,7 @@ Json Json::ParseFile( const std::string& filename )
json = ::json_tokener_parse_ex( tok, buf, count ) ;
if ( json == 0 )
throw Error() << expt::ErrMsg( ::json_tokener_errors[tok->err] ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( ::json_tokener_errors[tok->err] ) ) ;
::json_tokener_free( tok ) ;
@ -152,7 +161,10 @@ Json Json::operator[]( const std::string& key ) const
struct json_object *j = ::json_object_object_get( m_json, key.c_str() ) ;
if ( j == 0 )
throw Error() << expt::ErrMsg( "key: " + key + " is not found in object" ) ;
BOOST_THROW_EXCEPTION(
Error()
<< expt::ErrMsg( "key: " + key + " is not found in object" )
<< JsonInfo( *this ) ) ;
return Json( j ) ;
}
@ -166,7 +178,10 @@ Json Json::operator[]( const std::size_t& idx ) const
{
std::ostringstream ss ;
ss << "index " << idx << " is not found in array" ;
throw Error() << expt::ErrMsg( ss.str() ) ;
BOOST_THROW_EXCEPTION(
Error()
<< expt::ErrMsg( ss.str() )
<< JsonInfo( *this ) ) ;
}
return Json( j ) ;
@ -294,7 +309,8 @@ Json Json::FindInArray( const std::string& key, const std::string& value ) const
if ( item.Has(key) && item[key].Str() == value )
return item ;
}
throw Error() << expt::ErrMsg( "cannot find " + key + " = " + value + " in array" ) ;
BOOST_THROW_EXCEPTION(
Error() << expt::ErrMsg( "cannot find " + key + " = " + value + " in array" ) ) ;
}
bool Json::FindInArray( const std::string& key, const std::string& value, Json& result ) const

View File

@ -36,6 +36,7 @@ public :
typedef std::vector<Json> Array ;
struct Error : virtual Exception {} ;
typedef boost::error_info<struct JsonTag, Json> JsonInfo ;
public :
template <typename T>

View File

@ -19,6 +19,8 @@
#include "DateTime.hh"
#include <sstream>
#include <cassert>
#include <cstdlib>
#include <cstring>
@ -149,4 +151,11 @@ void DateTime::Swap( DateTime& dt )
std::swap( m_nsec, dt.m_nsec ) ;
}
std::string DateTime::ToString() const
{
std::ostringstream ss ;
ss << *this ;
return ss.str() ;
}
} // end of namespace

View File

@ -51,6 +51,7 @@ public :
bool operator<=( const DateTime& dt ) const ;
void Swap( DateTime& dt ) ;
std::string ToString() const ;
private :
std::time_t m_sec ;

View File

@ -92,7 +92,7 @@ public :
// cannot allow duplicate attribute nodes
if ( child->m_type == attr && p.first != p.second )
throw Error() << expt::ErrMsg( "duplicate attribute " + child->m_name ) ;
BOOST_THROW_EXCEPTION( Error() << expt::ErrMsg( "duplicate attribute " + child->m_name ) ) ;
vec.insert( p.second, child ) ;
}

View File

@ -22,6 +22,7 @@
#include "util/DefaultLog.hh"
#include "drive/EntryTest.hh"
#include "drive/ResourceTreeTest.hh"
#include "drive/StateTest.hh"
#include "util/DateTimeTest.hh"
#include "util/FunctionTest.hh"
@ -40,6 +41,7 @@ int main( int argc, char **argv )
CppUnit::TextUi::TestRunner runner;
runner.addTest( EntryTest::suite( ) ) ;
runner.addTest( StateTest::suite( ) ) ;
runner.addTest( ResourceTreeTest::suite( ) ) ;
runner.addTest( DateTimeTest::suite( ) ) ;
runner.addTest( FunctionTest::suite( ) ) ;
runner.addTest( SignalHandlerTest::suite( ) ) ;

View File

@ -0,0 +1,42 @@
/*
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 "ResourceTreeTest.hh"
#include "Assert.hh"
#include "drive/ResourceTree.hh"
#include "drive/Resource.hh"
#include <iostream>
namespace grut {
using namespace gr ;
ResourceTreeTest::ResourceTreeTest( )
{
}
void ResourceTreeTest::TestSerialize( )
{
}
} // end of namespace grut

View File

@ -0,0 +1,41 @@
/*
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 <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
namespace grut {
class ResourceTreeTest : public CppUnit::TestFixture
{
public :
ResourceTreeTest( ) ;
// declare suit function
CPPUNIT_TEST_SUITE( ResourceTreeTest ) ;
CPPUNIT_TEST( TestSerialize ) ;
CPPUNIT_TEST_SUITE_END();
private :
void TestSerialize( ) ;
} ;
} // end of namespace