HistoryLogReader: new HistoryLogData based API for parsing the new history file entri...
authorMichael Andres <ma@suse.de>
Mon, 3 Dec 2012 13:31:33 +0000 (14:31 +0100)
committerMichael Andres <ma@suse.de>
Mon, 3 Dec 2012 13:31:33 +0000 (14:31 +0100)
The old HistoryItem based API is deprecated but will still be available
for a while if you compile with -DWITH_DEPRECATED_HISTORYITEM_API.

tests/parser/HistoryLogReader_test.cc
tests/parser/HistoryLogReader_test.dat
zypp/CMakeLists.txt
zypp/HistoryLogData.cc
zypp/HistoryLogData.h
zypp/parser/HistoryLogReader.cc
zypp/parser/HistoryLogReader.h

index d3522c9..ad9c247 100644 (file)
@@ -1,29 +1,75 @@
 #include "TestSetup.h"
+#define WITH_DEPRECATED_HISTORYITEM_API
 #include "zypp/parser/HistoryLogReader.h"
 #include "zypp/parser/ParseException.h"
 
 using namespace zypp;
 
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
 namespace
 {
-  bool ProcessItem( const HistoryItem::Ptr & ptr )
+  bool OldApi_ProcessItem( const HistoryItem::Ptr & ptr )
   {
     DBG << ptr << endl;
     return true;
   }
 }
 
-// Must be the first test!
+BOOST_AUTO_TEST_CASE(OldApi_basic)
+{
+  parser::HistoryLogReader parser( TESTS_SRC_DIR "/parser/HistoryLogReader_test.dat",
+                                  OldApi_ProcessItem );
+
+  BOOST_CHECK_EQUAL( parser.ignoreInvalidItems(), false );
+  BOOST_CHECK_THROW( parser.readAll(), parser::ParseException );
+
+  parser.setIgnoreInvalidItems( true );
+  BOOST_CHECK_EQUAL( parser.ignoreInvalidItems(), true );
+  parser.readAll();
+}
+
+#endif // WITH_DEPRECATED_HISTORYITEM_API
+
+namespace
+{
+  bool ProcessData( const HistoryLogData::Ptr & ptr )
+  {
+    DBG << ptr->date() << " | " << ptr << endl;
+
+    return true;
+  }
+}
+
+
 BOOST_AUTO_TEST_CASE(basic)
 {
+  std::vector<HistoryLogData::Ptr> history;
   parser::HistoryLogReader parser( TESTS_SRC_DIR "/parser/HistoryLogReader_test.dat",
-                                  ProcessItem );
+                                  parser::HistoryLogReader::Options(),
+    [&history]( HistoryLogData::Ptr ptr )->bool {
+      history.push_back( ptr );
+      return true;
+    } );
 
   BOOST_CHECK_EQUAL( parser.ignoreInvalidItems(), false );
   BOOST_CHECK_THROW( parser.readAll(), parser::ParseException );
 
   parser.setIgnoreInvalidItems( true );
   BOOST_CHECK_EQUAL( parser.ignoreInvalidItems(), true );
+
+  history.clear();
   parser.readAll();
 
+  BOOST_CHECK_EQUAL( history.size(), 7 );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogDataRepoAdd>     ( history[0] ) );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogDataInstall>     ( history[1] ) );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogDataInstall>     ( history[2] ) );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogDataRemove>      ( history[3] ) );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogDataRepoRemove>  ( history[4] ) );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogDataRemove>      ( history[5] ) );
+  BOOST_CHECK( dynamic_pointer_cast<HistoryLogData>            ( history[6] ) );
+
+  BOOST_CHECK_EQUAL( (*history[1])[HistoryLogDataInstall::USERDATA_INDEX], "trans|ID" ); // properly (un)escaped?
+  HistoryLogDataInstall::Ptr p = dynamic_pointer_cast<HistoryLogDataInstall>( history[1] );
+  BOOST_CHECK_EQUAL( p->userdata(), "trans|ID" ); // properly (un)escaped?
 }
index 50ad6fc..26c7d67 100644 (file)
@@ -1,5 +1,5 @@
 2009-09-29 07:24:46|radd   |InstallationImage|file:/usr/src/packages/BUILD/openSUSE-images-11.2/gnome_cd/home/rpmdir|
-2009-09-29 07:25:29|install|update-test-security|0-2.35|noarch|root@opensuse|InstallationImage|d99de2872270cbd436b0c10af85c286a1365a348|
+2009-09-29 07:25:29|install|update-test-security|0-2.35|noarch|root@opensuse|InstallationImage|d99de2872270cbd436b0c10af85c286a1365a348|trans\|ID
 2011-07-18 18:08:09|install|kernel-source|2.6.37.6-0.5.1|noarch||repo-update|7823612d9ca3de086c2cd4dca936f6a3f3e9313d|
 # 2009-09-29 07:25:30 filesystem.rpm installed ok
 # Additional rpm output:
@@ -7,5 +7,6 @@
 2009-09-29 07:47:59|remove |PolicyKit-doc|0.9-15.20|x86_64|root@opensuse|trans\|ID
 2009-09-29 07:47:32|rremove|InstallationImage|
 2010-06-01 16:11:17|remove |xchat-python|2.8.6-43.13|x86_64|2848:y2base|
-2010-06-01 16:11:17|bad |xchat-python|2.8.6-43.13|x86_64|2848:y2base|
-[/]
+2010-06-01 16:11:17|bad |unknown action field
+discard\|one field
+discard|to fields but bad date
index fe78e42..538a7c4 100644 (file)
@@ -4,6 +4,9 @@
 
 ADD_DEFINITIONS(-DLOCALEDIR="${CMAKE_INSTALL_PREFIX}/share/locale" -DTEXTDOMAIN="zypp" -DZYPP_DLL )
 
+# 2012-12-03: HistoryLogReader: deprecate old API based in HistoryItem; use HistoryLogData based one
+ADD_DEFINITIONS(-DWITH_DEPRECATED_HISTORYITEM_API=1)
+
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
 #FILE(WRITE filename "message to write"... )
 
index 0e0f69e..3c73af5 100644 (file)
 
 using namespace std;
 
+///////////////////////////////////////////////////////////////////
 namespace zypp
 {
   using parser::ParseException;
 
-
   ///////////////////////////////////////////////////////////////////
   //
-  //    CLASS NAME : HistoryActionID
+  //   class HistoryActionID
   //
   ///////////////////////////////////////////////////////////////////
 
@@ -61,12 +61,11 @@ namespace zypp
     }
 
     MapType::const_iterator it = _table.find( strval_r );
-    if ( it == _table.end() )
-    {
-      WAR << "Unknown history action ID '" + strval_r + "'" << endl;
-      return NONE_e;
-    }
-    return it->second;
+    if ( it != _table.end() )
+      return it->second;
+    // else:
+    WAR << "Unknown history action ID '" + strval_r + "'" << endl;
+    return NONE_e;
   }
 
 
@@ -95,13 +94,215 @@ namespace zypp
 
   ///////////////////////////////////////////////////////////////////
 
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   class HistoryLogData::Impl
+  //
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogData::Impl
+  {
+  public:
+    Impl( FieldVector & fields_r, size_type expect_r )
+    {
+      _checkFields( fields_r, expect_r );
+      _field.swap( fields_r );
+      // For whatever reason writer is ' '-padding the action field
+      // but we don't want to modify the vector before we moved it.
+      _field[ACTION_INDEX] = str::trim( _field[ACTION_INDEX] );
+      _action = HistoryActionID( _field[ACTION_INDEX] );
+    }
 
-  /////////////////////////////////////////////////////////////////////
+    Impl( FieldVector & fields_r, HistoryActionID action_r, size_type expect_r )
+    {
+      _checkFields( fields_r, expect_r );
+      // For whatever reason writer is ' '-padding the action field
+      // but we don't want to modify the vector before we moved it.
+      std::string trimmed( str::trim( fields_r[ACTION_INDEX] ) );
+      _action = HistoryActionID( trimmed );
+      if ( _action != action_r )
+      {
+       ZYPP_THROW( ParseException( str::form( "Bad action id. Got %s, expected %s.",
+                                              _action.asString().c_str(),
+                                              action_r.asString().c_str() ) ) );
+      }
+      _field.swap( fields_r );
+      // now adjust action field:
+      _field[ACTION_INDEX] = trimmed;
+    }
+
+    void _checkFields( const FieldVector & fields_r, size_type expect_r )
+    {
+      if ( expect_r < 2 )      // at least 2 fields (date and action) are required
+       expect_r = 2;
+      if ( fields_r.size() < expect_r )
+      {
+       ZYPP_THROW( ParseException( str::form( "Bad number of fields. Got %zd, expected at least %zd.",
+                                              fields_r.size(),
+                                              expect_r ) ) );
+      }
+      try
+      {
+       _date = Date( fields_r[DATE_INDEX], HISTORY_LOG_DATE_FORMAT );
+      }
+      catch ( const std::exception & excpt )
+      {
+       ZYPP_THROW( ParseException( excpt.what() ) );   // invalid date format
+      }
+      // _action handled later
+    }
+
+  public:
+    FieldVector        _field;
+    Date               _date;
+    HistoryActionID    _action;
+  };
+
+  ///////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItem
+  //   class HistoryLogData
   //
-  /////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////
+
+  HistoryLogData::HistoryLogData( FieldVector & fields_r, size_type expect_r )
+  : _pimpl( new Impl( fields_r, expect_r ) )
+  {}
+
+  HistoryLogData::HistoryLogData( FieldVector & fields_r, HistoryActionID expectedId_r, size_type expect_r )
+  : _pimpl( new Impl( fields_r, expectedId_r, expect_r ) )
+  {}
+
+  HistoryLogData::~HistoryLogData()
+  {}
+
+  HistoryLogData::Ptr HistoryLogData::create( FieldVector & fields_r )
+  {
+    if ( fields_r.size() >= 2 )
+    {
+      // str::trim( _field[ACTION_INDEX] );
+      switch ( HistoryActionID( str::trim( fields_r[ACTION_INDEX] ) ).toEnum() )
+      {
+#define OUTS(E,T) case HistoryActionID::E: return Ptr( new T( fields_r ) ); break;
+       OUTS( INSTALL_e,                HistoryLogDataInstall );
+       OUTS( REMOVE_e,                 HistoryLogDataRemove );
+       OUTS( REPO_ADD_e,               HistoryLogDataRepoAdd );
+       OUTS( REPO_REMOVE_e,            HistoryLogDataRepoRemove );
+       OUTS( REPO_CHANGE_ALIAS_e,      HistoryLogDataRepoAliasChange );
+       OUTS( REPO_CHANGE_URL_e,        HistoryLogDataRepoUrlChange );
+#undef OUTS
+       // intentionally no default:
+       case HistoryActionID::NONE_e:
+         break;
+      }
+    }
+    // unknown action or invalid fields? Ctor will accept or throw.
+    return Ptr( new HistoryLogData( fields_r ) );
+  }
+
+  bool HistoryLogData::empty() const
+  { return _pimpl->_field.empty(); }
+
+  HistoryLogData::size_type HistoryLogData::size() const
+  { return _pimpl->_field.size(); }
 
+  HistoryLogData::const_iterator HistoryLogData::begin() const
+  { return _pimpl->_field.begin(); }
+
+  HistoryLogData::const_iterator HistoryLogData::end() const
+  { return _pimpl->_field.end(); }
+
+  const std::string & HistoryLogData::optionalAt( size_type idx_r ) const
+  {
+    static const std::string _empty;
+    return( idx_r < size() ? _pimpl->_field[idx_r] : _empty );
+  }
+
+  const std::string & HistoryLogData::at( size_type idx_r ) const
+  { return _pimpl->_field.at( idx_r ); }
+
+
+  Date HistoryLogData::date() const
+  { return _pimpl->_date; }
+
+  HistoryActionID HistoryLogData::action() const
+  { return _pimpl->_action; }
+
+
+  std::ostream & operator<<( std::ostream & str, const HistoryLogData & obj )
+  { return str << str::joinEscaped( obj.begin(), obj.end(), '|' ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //   class HistoryLogDataInstall
+  ///////////////////////////////////////////////////////////////////
+    HistoryLogDataInstall::HistoryLogDataInstall( FieldVector & fields_r )
+    : HistoryLogData( fields_r )
+    {}
+    std::string        HistoryLogDataInstall::name()           const { return optionalAt( NAME_INDEX ); }
+    Edition    HistoryLogDataInstall::edition()        const { return Edition( optionalAt( EDITION_INDEX ) ); }
+    Arch       HistoryLogDataInstall::arch()           const { return Arch( optionalAt( ARCH_INDEX ) ); }
+    std::string        HistoryLogDataInstall::reqby()          const { return optionalAt( REQBY_INDEX ); }
+    std::string        HistoryLogDataInstall::repoAlias()      const { return optionalAt( REPOALIAS_INDEX ); }
+    CheckSum   HistoryLogDataInstall::checksum()       const { return optionalAt( CHEKSUM_INDEX ); }
+    std::string        HistoryLogDataInstall::userdata()       const { return optionalAt( USERDATA_INDEX ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //   class HistoryLogDataRemove
+  ///////////////////////////////////////////////////////////////////
+    HistoryLogDataRemove::HistoryLogDataRemove( FieldVector & fields_r )
+    : HistoryLogData( fields_r )
+    {}
+    std::string        HistoryLogDataRemove::name()            const { return optionalAt( NAME_INDEX ); }
+    Edition    HistoryLogDataRemove::edition()         const { return Edition( optionalAt( EDITION_INDEX ) ); }
+    Arch       HistoryLogDataRemove::arch()            const { return Arch( optionalAt( ARCH_INDEX ) ); }
+    std::string        HistoryLogDataRemove::reqby()           const { return optionalAt( REQBY_INDEX ); }
+    std::string        HistoryLogDataRemove::userdata()        const { return optionalAt( USERDATA_INDEX ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //   class HistoryLogDataRepoAdd
+  ///////////////////////////////////////////////////////////////////
+    HistoryLogDataRepoAdd::HistoryLogDataRepoAdd( FieldVector & fields_r )
+    : HistoryLogData( fields_r )
+    {}
+    std::string        HistoryLogDataRepoAdd::alias()          const { return optionalAt( ALIAS_INDEX ); }
+    Url                HistoryLogDataRepoAdd::url()            const { return optionalAt( URL_INDEX ); }
+    std::string        HistoryLogDataRepoAdd::userdata()       const { return optionalAt( USERDATA_INDEX ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //   class HistoryLogDataRepoRemove
+  ///////////////////////////////////////////////////////////////////
+    HistoryLogDataRepoRemove::HistoryLogDataRepoRemove( FieldVector & fields_r )
+    : HistoryLogData( fields_r )
+    {}
+    std::string        HistoryLogDataRepoRemove::alias()       const { return optionalAt( ALIAS_INDEX ); }
+    std::string        HistoryLogDataRepoRemove::userdata()    const { return optionalAt( USERDATA_INDEX ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //   class HistoryLogDataRepoAliasChange
+  ///////////////////////////////////////////////////////////////////
+    HistoryLogDataRepoAliasChange::HistoryLogDataRepoAliasChange( FieldVector & fields_r )
+    : HistoryLogData( fields_r )
+    {}
+    std::string        HistoryLogDataRepoAliasChange::oldAlias()       const { return optionalAt( OLDALIAS_INDEX ); }
+    std::string        HistoryLogDataRepoAliasChange::newAlias()       const { return optionalAt( NEWALIAS_INDEX ); }
+    std::string        HistoryLogDataRepoAliasChange::userdata()       const { return optionalAt( USERDATA_INDEX ); }
+
+  ///////////////////////////////////////////////////////////////////
+  //   class HistoryLogDataRepoUrlChange
+  ///////////////////////////////////////////////////////////////////
+    HistoryLogDataRepoUrlChange::HistoryLogDataRepoUrlChange( FieldVector & fields_r )
+    : HistoryLogData( fields_r )
+    {}
+    std::string        HistoryLogDataRepoUrlChange::alias()    const { return optionalAt( ALIAS_INDEX ); }
+    Url                HistoryLogDataRepoUrlChange::newUrl()   const { return optionalAt( NEWURL_INDEX ); }
+    std::string        HistoryLogDataRepoUrlChange::userdata() const { return optionalAt( USERDATA_INDEX ); }
+
+
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryItem
+  /// \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class.
+  /// They grant direct access to data members, so can not be extended
+  /// without losing binary compatibility.
+  ///////////////////////////////////////////////////////////////////
   HistoryItem::HistoryItem(FieldVector & fields)
   {
     if (fields.size() <= 2)
@@ -127,7 +328,7 @@ namespace zypp
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItemInstall
+  // CLASS NAME: HistoryItemInstall (deprecated!)
   //
   /////////////////////////////////////////////////////////////////////
 
@@ -168,7 +369,7 @@ namespace zypp
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItemRemove
+  // CLASS NAME: HistoryItemRemove (deprecated!)
   //
   /////////////////////////////////////////////////////////////////////
 
@@ -205,7 +406,7 @@ namespace zypp
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItemRepoAdd
+  // CLASS NAME: HistoryItemRepoAdd (deprecated!)
   //
   /////////////////////////////////////////////////////////////////////
 
@@ -238,7 +439,7 @@ namespace zypp
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItemRepoRemove
+  // CLASS NAME: HistoryItemRepoRemove (deprecated!)
   //
   /////////////////////////////////////////////////////////////////////
 
@@ -268,7 +469,7 @@ namespace zypp
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItemRepoAliasChange
+  // CLASS NAME: HistoryItemRepoAliasChange (deprecated!)
   //
   /////////////////////////////////////////////////////////////////////
 
@@ -299,7 +500,7 @@ namespace zypp
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryItemRepoUrlChange
+  // CLASS NAME: HistoryItemRepoUrlChange (deprecated!)
   //
   /////////////////////////////////////////////////////////////////////
 
@@ -326,6 +527,7 @@ namespace zypp
     obj.dumpTo(str);
     return str;
   }
+#endif // WITH_DEPRECATED_HISTORYITEM_API
 
-
-}
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
index 8ce5880..2ef5e21 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <iosfwd>
 
+#include "zypp/APIConfig.h"
 #include "zypp/Date.h"
 #include "zypp/Edition.h"
 #include "zypp/Arch.h"
@@ -23,6 +24,7 @@
 
 #define HISTORY_LOG_DATE_FORMAT "%Y-%m-%d %H:%M:%S"
 
+///////////////////////////////////////////////////////////////////
 namespace zypp
 {
   ///////////////////////////////////////////////////////////////////
@@ -71,14 +73,323 @@ namespace zypp
   };
 
   /** \relates HistoryActionID */
+  inline bool operator==( const HistoryActionID & lhs, const HistoryActionID & rhs )
+  { return lhs.toEnum() == rhs.toEnum(); }
+
+  /** \relates HistoryActionID */
+  inline bool operator!=( const HistoryActionID & lhs, const HistoryActionID & rhs )
+  { return lhs.toEnum() != rhs.toEnum(); }
+
+  /** \relates HistoryActionID */
   std::ostream & operator << (std::ostream & str, const HistoryActionID & id);
   ///////////////////////////////////////////////////////////////////
 
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogData
+  /// \brief A zypp history log line split into fields
+  /// \ingroup g_ZyppHistory
+  ///
+  /// Each valid history log line starts with a date and HistoryActionID
+  /// field. Subsequent fields depend on the kind of action. See derived
+  /// classes for convenient access to those flields.
+  ///
+  /// HistoryLogData itself provides mostly generic access to the fields
+  /// plain string values. Derived classes for well known entries tell
+  ///
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogData>         Ptr;
+    typedef shared_ptr<const HistoryLogData>   constPtr;
+
+    typedef std::vector<std::string>   FieldVector;
+    typedef FieldVector::size_type     size_type;
+    typedef FieldVector::const_iterator        const_iterator;
 
-  /////////////////////////////////////////////////////////////////////
-  //
-  // CLASS NAME: HistoryItem
-  //
+  public:
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has not at least \a expect_r entries
+     * \note 2 fields (date and action) are always required.
+     */
+    explicit HistoryLogData( FieldVector & fields_r, size_type expect_r = 2 );
+
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or not at least \a expect_r entries.
+     * \note 2 fields (date and action) are always required.
+     */
+    HistoryLogData( FieldVector & fields_r, HistoryActionID action_r, size_type expect_r = 2 );
+
+    /** Dtor */
+    virtual ~HistoryLogData();
+
+    /** Factory method creating HistoryLogData classes.
+     *
+     * Moves \a fields_r into a HistoryLogData or derived object, depending on the
+     * HistoryActionID. For known action ids a coresponing HistoryLogData class
+     * is created, to allow convenient access to the field values. For unknown
+     * action ids a plain HistoryLogData object is created. \ref HistoryActionID
+     * \ref NONE_e id used in this case.
+     *
+     * \throws ParseException if \a fields_r does not contain the required format.
+     */
+    static Ptr create( FieldVector & fields_r );
+
+  public:
+    /** Whether FieldVector is empty. */
+    bool empty() const;
+
+    /** Number of fields in vector. */
+    size_type size() const;
+
+    /** Iterator pointing to 1st element in vector (or end()). */
+    const_iterator begin() const;
+
+    /** Iterator pointing behind the last element in vector. */
+    const_iterator end() const;
+
+    /** Access (optional) field by number.
+     * \returns an empty string if \a idx_r is out of range.
+     * \see \ref at
+     */
+    const std::string & optionalAt( size_type idx_r ) const;
+    /** \overload */
+    const std::string & operator[]( size_type idx_r ) const
+    { return optionalAt( idx_r ); }
+
+    /** Access (required) field by number.
+     * \throws std::out_of_range if \a idx_r is out of range.
+     * \see \ref optionalAt
+     */
+    const std::string & at( size_type idx_r ) const;
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = 0,    ///< date
+      ACTION_INDEX     = 1,    ///< HistoryActionID
+    };
+
+  public:
+    Date       date()          const;  ///< date
+    HistoryActionID action()   const;  ///< HistoryActionID (or \c NONE_e if unknown)
+
+  public:
+    class Impl;                 ///< Implementation class
+  private:
+    RWCOW_pointer<Impl> _pimpl; ///< Pointer to implementation
+  protected:
+    HistoryLogData & operator=( const HistoryLogData & ); ///< no base class assign
+  };
+
+  /** \relates HistoryLogData Stream output */
+  std::ostream & operator<<( std::ostream & str, const HistoryLogData & obj );
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogDataInstall
+  /// \brief  A zypp history log line for an installed packaged.
+  /// \ingroup g_ZyppHistory
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogDataInstall : public HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogDataInstall>          Ptr;
+    typedef shared_ptr<const HistoryLogDataInstall>    constPtr;
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or number of fields.
+     */
+    HistoryLogDataInstall( FieldVector & fields_r );
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = HistoryLogData::DATE_INDEX,
+      ACTION_INDEX     = HistoryLogData::ACTION_INDEX,
+      NAME_INDEX,              ///< package name
+      EDITION_INDEX,           ///< package edition
+      ARCH_INDEX,              ///< package architecture
+      REQBY_INDEX,             ///< requested by (user@hostname, pid:appname, or empty (solver))
+      REPOALIAS_INDEX,         ///< repository providing the package
+      CHEKSUM_INDEX,           ///< package checksum
+      USERDATA_INDEX,          ///< userdata/transactionID
+    };
+
+   public:
+    std::string        name()          const;  ///< package name
+    Edition    edition()       const;  ///< package edition
+    Arch       arch()          const;  ///< package architecture
+    std::string        reqby()         const;  ///< requested by (user@hostname, pid:appname, or empty (solver))
+    std::string        repoAlias()     const;  ///< repository providing the package
+    CheckSum   checksum()      const;  ///< package checksum
+    std::string        userdata()      const;  ///< userdata/transactionID
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogDataRemove
+  /// \brief A zypp history log line for a removed packge.
+  /// \ingroup g_ZyppHistory
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogDataRemove : public HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogDataRemove>           Ptr;
+    typedef shared_ptr<const HistoryLogDataRemove>     constPtr;
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or number of fields.
+     */
+    HistoryLogDataRemove( FieldVector & fields_r );
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = HistoryLogData::DATE_INDEX,
+      ACTION_INDEX     = HistoryLogData::ACTION_INDEX,
+      NAME_INDEX,              ///< package name
+      EDITION_INDEX,           ///< package edition
+      ARCH_INDEX,              ///< package architecture
+      REQBY_INDEX,             ///< requested by (user@hostname, pid:appname, or empty (solver))
+      USERDATA_INDEX,          ///< userdata/transactionID
+    };
+
+  public:
+    std::string        name()          const;  ///< package name
+    Edition    edition()       const;  ///< package edition
+    Arch       arch()          const;  ///< package architecture
+    std::string        reqby()         const;  ///< requested by (user@hostname, pid:appname, or empty (solver))
+    std::string        userdata()      const;  ///< userdata/transactionID
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogDataRepoAdd
+  /// \brief A zypp history log line for an added repository.
+  /// \ingroup g_ZyppHistory
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogDataRepoAdd : public HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogDataRepoAdd>          Ptr;
+    typedef shared_ptr<const HistoryLogDataRepoAdd>    constPtr;
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or number of fields.
+     */
+    HistoryLogDataRepoAdd( FieldVector & fields_r );
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = HistoryLogData::DATE_INDEX,
+      ACTION_INDEX     = HistoryLogData::ACTION_INDEX,
+      ALIAS_INDEX,             ///< repository alias
+      URL_INDEX,               ///< repository url
+      USERDATA_INDEX,          ///< userdata/transactionID
+    };
+
+  public:
+    std::string        alias()         const;  ///< repository alias
+    Url                url()           const;  ///< repository url
+    std::string        userdata()      const;  ///< userdata/transactionID
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogDataRepoRemove
+  /// \brief A zypp history log line for a removed repository.
+  /// \ingroup g_ZyppHistory
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogDataRepoRemove : public HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogDataRepoRemove>       Ptr;
+    typedef shared_ptr<const HistoryLogDataRepoRemove> constPtr;
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or number of fields.
+     */
+    HistoryLogDataRepoRemove( FieldVector & fields_r );
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = HistoryLogData::DATE_INDEX,
+      ACTION_INDEX     = HistoryLogData::ACTION_INDEX,
+      ALIAS_INDEX,             ///< repository alias
+      USERDATA_INDEX,          ///< userdata/transactionID
+    };
+
+  public:
+    std::string        alias()         const;  ///< repository alias
+    std::string        userdata()      const;  ///< userdata/transactionID
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogDataRepoAliasChange
+  /// \brief A zypp history log line for a repo alias change.
+  /// \ingroup g_ZyppHistory
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogDataRepoAliasChange : public HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogDataRepoAliasChange>          Ptr;
+    typedef shared_ptr<const HistoryLogDataRepoAliasChange>    constPtr;
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or number of fields.
+     */
+    HistoryLogDataRepoAliasChange( FieldVector & fields_r );
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = HistoryLogData::DATE_INDEX,
+      ACTION_INDEX     = HistoryLogData::ACTION_INDEX,
+      OLDALIAS_INDEX,          ///< repositories old alias
+      NEWALIAS_INDEX,          ///< repositories new alias
+      USERDATA_INDEX,          ///< userdata/transactionID
+   };
+
+  public:
+    std::string        oldAlias()      const;  ///< repositories old alias
+    std::string        newAlias()      const;  ///< repositories new alias
+    std::string        userdata()      const;  ///< userdata/transactionID
+  };
+
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryLogDataRepoUrlChange
+  /// \brief A zypp history log line for a repo url change.
+  /// \ingroup g_ZyppHistory
+  ///////////////////////////////////////////////////////////////////
+  class HistoryLogDataRepoUrlChange : public HistoryLogData
+  {
+  public:
+    typedef shared_ptr<HistoryLogDataRepoUrlChange>            Ptr;
+    typedef shared_ptr<const HistoryLogDataRepoUrlChange>      constPtr;
+    /** Ctor \b moving \a FieldVector (via swap).
+     * \throws ParseException if \a fields_r has the wrong \ref HistoryActionID or number of fields.
+     */
+    HistoryLogDataRepoUrlChange( FieldVector & fields_r );
+
+  public:
+    enum Index                 ///< indices of known fields
+    {
+      DATE_INDEX       = HistoryLogData::DATE_INDEX,
+      ACTION_INDEX     = HistoryLogData::ACTION_INDEX,
+      ALIAS_INDEX,             ///< repository alias
+      NEWURL_INDEX,            ///< repositories new url
+      USERDATA_INDEX,          ///< userdata/transactionID
+   };
+
+  public:
+    std::string        alias()         const;  ///< repository alias
+    Url                newUrl()        const;  ///< repositories new url
+    std::string        userdata()      const;  ///< userdata/transactionID
+  };
+
+
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+  ///////////////////////////////////////////////////////////////////
+  /// \class HistoryItem
+  /// \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class.
+  /// They grant direct access to data members, so can not be extended
+  /// without losing binary compatibility.
+  ///////////////////////////////////////////////////////////////////
   class HistoryItem
   {
   public:
@@ -103,7 +414,8 @@ namespace zypp
   //
   // CLASS NAME: HistoryItemInstall
   //
-  class HistoryItemInstall : public HistoryItem
+  /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+  class ZYPP_DEPRECATED HistoryItemInstall : public HistoryItem
   {
   public:
     typedef shared_ptr<HistoryItemInstall> Ptr;
@@ -129,7 +441,8 @@ namespace zypp
   //
   // CLASS NAME: HistoryItemRemove
   //
-  class HistoryItemRemove : public HistoryItem
+  /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+  class ZYPP_DEPRECATED HistoryItemRemove : public HistoryItem
   {
   public:
     typedef shared_ptr<HistoryItemRemove> Ptr;
@@ -153,7 +466,8 @@ namespace zypp
   //
   // CLASS NAME: HistoryItemRepoAdd
   //
-  class HistoryItemRepoAdd : public HistoryItem
+  /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+  class ZYPP_DEPRECATED HistoryItemRepoAdd : public HistoryItem
   {
   public:
     typedef shared_ptr<HistoryItemRepoAdd> Ptr;
@@ -175,7 +489,8 @@ namespace zypp
   //
   // CLASS NAME: HistoryItemRepoRemove
   //
-  class HistoryItemRepoRemove : public HistoryItem
+  /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+  class ZYPP_DEPRECATED HistoryItemRepoRemove : public HistoryItem
   {
   public:
     typedef shared_ptr<HistoryItemRepoRemove> Ptr;
@@ -196,7 +511,8 @@ namespace zypp
   //
   // CLASS NAME: HistoryItemRepoAliasChange
   //
-  class HistoryItemRepoAliasChange : public HistoryItem
+  /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+  class ZYPP_DEPRECATED HistoryItemRepoAliasChange : public HistoryItem
   {
   public:
     typedef shared_ptr<HistoryItemRepoAliasChange> Ptr;
@@ -218,7 +534,8 @@ namespace zypp
   //
   // CLASS NAME: HistoryItemRepoUrlChange
   //
-  class HistoryItemRepoUrlChange : public HistoryItem
+  /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+  class ZYPP_DEPRECATED HistoryItemRepoUrlChange : public HistoryItem
   {
   public:
     typedef shared_ptr<HistoryItemRepoUrlChange> Ptr;
@@ -236,7 +553,8 @@ namespace zypp
   /////////////////////////////////////////////////////////////////////
 
   std::ostream & operator<<(std::ostream & str, const HistoryItem & obj);
+#endif // WITH_DEPRECATED_HISTORYITEM_API
 
-}
-
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
 #endif /* ZYPP_HISTORYLOGDATA_H_ */
index a3d2ff5..bc58561 100644 (file)
 
 #include "zypp/parser/HistoryLogReader.h"
 
-using namespace std;
+using std::endl;
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
   ///////////////////////////////////////////////////////////////////
   namespace parser
-  { /////////////////////////////////////////////////////////////////
-
+  {
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryLogReader::Impl
+  //   class HistoryLogReader::Impl
   //
   /////////////////////////////////////////////////////////////////////
-
   struct HistoryLogReader::Impl
   {
-    Impl( const Pathname & historyFile, const ProcessItem & callback );
-    ~Impl()
+    Impl( const Pathname & historyFile_r, const Options & options_r, const ProcessData & callback_r )
+    :  _filename( historyFile_r )
+    , _options( options_r )
+    , _callback( callback_r )
     {}
 
-    HistoryItem::Ptr createHistoryItem(HistoryItem::FieldVector & fields);
-    void parseLine(const string & line, unsigned int lineNr);
+    bool parseLine( const std::string & line_r, unsigned int lineNr_r );
 
-    void readAll( const ProgressData::ReceiverFnc & progress );
-    void readFrom( const Date & date, const ProgressData::ReceiverFnc & progress );
-    void readFromTo( const Date & fromDate, const Date & toDate, const ProgressData::ReceiverFnc & progress );
+    void readAll( const ProgressData::ReceiverFnc & progress_r );
+    void readFrom( const Date & date_r, const ProgressData::ReceiverFnc & progress_r );
+    void readFromTo( const Date & fromDate_r, const Date & toDate_r, const ProgressData::ReceiverFnc & progress_r );
 
     Pathname _filename;
-    ProcessItem _callback;
-    bool _ignoreInvalid;
+    Options  _options;
+    ProcessData _callback;
+
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+    ProcessItem _oldAPICallback;
+  private:
+    HistoryItem::Ptr _oldAPIcreateHistoryItem( HistoryItem::FieldVector & fields );
+    void _oldAPIparseLine(const std::string & line, unsigned int lineNr);
+#endif // WITH_DEPRECATED_HISTORYITEM_API
   };
 
+  bool HistoryLogReader::Impl::parseLine( const std::string & line_r, unsigned lineNr_r )
+  {
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+    if ( _oldAPICallback )
+    {
+      _oldAPIparseLine( line_r, lineNr_r );
+      return true;     // old api did not eavluate callback return value :(
+    }
+#endif // WITH_DEPRECATED_HISTORYITEM_API
+
+    // parse into fields
+    HistoryLogData::FieldVector fields;
+    str::splitEscaped( line_r, std::back_inserter(fields), "|", true );
+    if ( fields.size() >= 2 )
+      str::trim( fields[1] );  // for whatever reason writer is padding the action field
 
-  HistoryLogReader::Impl::Impl( const Pathname & historyFile, const ProcessItem & callback )
-    : _filename( historyFile )
-    , _callback( callback )
-    , _ignoreInvalid( false )
-  {}
+    // move into data class
+    HistoryLogData::Ptr data;
+    try
+    {
+      data = HistoryLogData::create( fields );
+    }
+    catch ( const Exception & excpt )
+    {
+      ZYPP_CAUGHT( excpt );
+      if ( _options.testFlag( IGNORE_INVALID_ITEMS ) )
+      {
+       WAR << "Ignore invalid history log entry on line #" << lineNr_r << " '"<< line_r << "'" << endl;
+       return true;
+      }
+      else
+      {
+       ERR << "Invalid history log entry on line #" << lineNr_r << " '"<< line_r << "'" << endl;
+       ParseException newexcpt( str::Str() << "Error in history log on line #" << lineNr_r );
+       newexcpt.remember( excpt );
+       ZYPP_THROW( newexcpt );
+      }
+    }
+
+    // consume data
+    if ( _callback && !_callback( data ) )
+    {
+      WAR << "Stop parsing requested by consumer callback on line #" << lineNr_r << endl;
+      return false;
+    }
+    return true;
+  }
+
+  void HistoryLogReader::Impl::readAll( const ProgressData::ReceiverFnc & progress_r )
+  {
+    InputStream is( _filename );
+    iostr::EachLine line( is );
+
+    ProgressData pd;
+    pd.sendTo( progress_r );
+    pd.toMin();
+
+    for ( ; line; line.next(), pd.tick() )
+    {
+      // ignore comments
+      if ( (*line)[0] == '#' )
+        continue;
+
+      if ( ! parseLine( *line, line.lineNo() ) )
+       break;  // requested by consumer callback
+    }
+
+    pd.toMax();
+  }
+
+  void HistoryLogReader::Impl::readFrom( const Date & date_r, const ProgressData::ReceiverFnc & progress_r )
+  {
+    InputStream is( _filename );
+    iostr::EachLine line( is );
+
+    ProgressData pd;
+    pd.sendTo( progress_r );
+    pd.toMin();
+
+    bool pastDate = false;
+    for ( ; line; line.next(), pd.tick() )
+    {
+      const std::string & s = *line;
+
+      // ignore comments
+      if ( s[0] == '#' )
+        continue;
+
+      if ( pastDate )
+      {
+       if ( ! parseLine( s, line.lineNo() ) )
+         break;        // requested by consumer callback
+      }
+      else
+      {
+        Date logDate( s.substr( 0, s.find('|') ), HISTORY_LOG_DATE_FORMAT );
+        if ( logDate > date_r )
+        {
+          pastDate = true;
+          if ( ! parseLine( s, line.lineNo() ) )
+           break;      // requested by consumer callback
+        }
+      }
+    }
+
+    pd.toMax();
+  }
+
+  void HistoryLogReader::Impl::readFromTo( const Date & fromDate_r, const Date & toDate_r, const ProgressData::ReceiverFnc & progress_r )
+  {
+    InputStream is( _filename );
+    iostr::EachLine line( is );
+
+    ProgressData pd;
+    pd.sendTo( progress_r );
+    pd.toMin();
+
+    bool pastFromDate = false;
+    for ( ; line; line.next(), pd.tick() )
+    {
+      const std::string & s = *line;
+
+      // ignore comments
+      if ( s[0] == '#' )
+        continue;
+
+      Date logDate( s.substr( 0, s.find('|') ), HISTORY_LOG_DATE_FORMAT );
+
+      // past toDate - stop reading
+      if ( logDate >= toDate_r )
+        break;
+
+      // past fromDate - start reading
+      if ( !pastFromDate && logDate > fromDate_r )
+        pastFromDate = true;
+
+      if ( pastFromDate )
+      {
+       if ( ! parseLine( s, line.lineNo() ) )
+         break;        // requested by consumer callback
+      }
+    }
 
+    pd.toMax();
+  }
 
-  HistoryItem::Ptr HistoryLogReader::Impl::createHistoryItem( HistoryItem::FieldVector & fields )
+
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+  HistoryItem::Ptr HistoryLogReader::Impl::_oldAPIcreateHistoryItem( HistoryItem::FieldVector & fields )
   {
     HistoryActionID aid( str::trim( fields[1] ) );
     switch ( aid.toEnum() )
@@ -95,9 +241,7 @@ namespace zypp
     }
     return HistoryItem::Ptr();
   }
-
-
-  void HistoryLogReader::Impl::parseLine( const string & line, unsigned int lineNr )
+  void HistoryLogReader::Impl::_oldAPIparseLine( const std::string & line, unsigned int lineNr )
   {
     // parse into fields
     HistoryItem::FieldVector fields;
@@ -105,7 +249,7 @@ namespace zypp
 
     if ( fields.size() <= 2 )
     {
-      if ( !_ignoreInvalid )
+      if ( ! _options.testFlag( IGNORE_INVALID_ITEMS ) )
       {
        ParseException e( str::form( "Error in history log on line #%u.", lineNr ) );
        e.addHistory( str::form( "Bad number of fields. Got %zd, expected more than %d.", fields.size(), 2 ) );
@@ -121,14 +265,14 @@ namespace zypp
     HistoryItem::Ptr item_ptr;
     try
     {
-      item_ptr = createHistoryItem( fields );
+      item_ptr = _oldAPIcreateHistoryItem( fields );
     }
     catch ( const Exception & e )
     {
       ZYPP_CAUGHT(e);
       ERR << "Invalid history log entry on line #" << lineNr << " '"<< line << "'" << endl;
 
-      if ( !_ignoreInvalid )
+      if ( ! _options.testFlag( IGNORE_INVALID_ITEMS ) )
       {
         ParseException newe( str::form( "Error in history log on line #%u.", lineNr ) );
        newe.remember( e );
@@ -138,9 +282,9 @@ namespace zypp
 
     if ( item_ptr )
     {
-      _callback( item_ptr );
+      _oldAPICallback( item_ptr );
     }
-    else if ( !_ignoreInvalid )
+    else if ( ! _options.testFlag( IGNORE_INVALID_ITEMS ) )
     {
       ParseException e( str::form( "Error in history log on line #%u.", lineNr ) );
       e.addHistory( "Unknown entry type." );
@@ -151,137 +295,44 @@ namespace zypp
       WAR << "Unknown history log action type: " << fields[1] << " on line #" << lineNr << endl;
     }
   }
-
-
-  void HistoryLogReader::Impl::readAll(const ProgressData::ReceiverFnc & progress)
-  {
-    InputStream is(_filename);
-    iostr::EachLine line(is);
-
-    ProgressData pd;
-    pd.sendTo( progress );
-    pd.toMin();
-
-    for (; line; line.next(), pd.tick() )
-    {
-      // ignore comments
-      if ((*line)[0] == '#')
-        continue;
-
-      parseLine(*line, line.lineNo());
-    }
-
-    pd.toMax();
-  }
-
-  void HistoryLogReader::Impl::readFrom(const Date & date,
-      const ProgressData::ReceiverFnc & progress)
-  {
-    InputStream is(_filename);
-    iostr::EachLine line(is);
-
-    ProgressData pd;
-    pd.sendTo( progress );
-    pd.toMin();
-
-    bool pastDate = false;
-    for (; line; line.next(), pd.tick())
-    {
-      const string & s = *line;
-
-      // ignore comments
-      if (s[0] == '#')
-        continue;
-
-      if (pastDate)
-        parseLine(s, line.lineNo());
-      else
-      {
-        Date logDate(s.substr(0, s.find('|')), HISTORY_LOG_DATE_FORMAT);
-        if (logDate > date)
-        {
-          pastDate = true;
-          parseLine(s, line.lineNo());
-        }
-      }
-    }
-
-    pd.toMax();
-  }
-
-  void HistoryLogReader::Impl::readFromTo(
-      const Date & fromDate, const Date & toDate,
-      const ProgressData::ReceiverFnc & progress)
-  {
-    InputStream is(_filename);
-    iostr::EachLine line(is);
-
-    ProgressData pd;
-    pd.sendTo(progress);
-    pd.toMin();
-
-    bool pastFromDate = false;
-    for (; line; line.next(), pd.tick())
-    {
-      const string & s = *line;
-
-      // ignore comments
-      if (s[0] == '#')
-        continue;
-
-      Date logDate(s.substr(0, s.find('|')), HISTORY_LOG_DATE_FORMAT);
-
-      // past toDate - stop reading
-      if (logDate >= toDate)
-        break;
-
-      // past fromDate - start reading
-      if (!pastFromDate && logDate > fromDate)
-        pastFromDate = true;
-
-      if (pastFromDate)
-        parseLine(s, line.lineNo());
-    }
-
-    pd.toMax();
-  }
+#endif // WITH_DEPRECATED_HISTORYITEM_API
 
   /////////////////////////////////////////////////////////////////////
   //
-  // CLASS NAME: HistoryLogReader
+  //   class HistoryLogReader
   //
   /////////////////////////////////////////////////////////////////////
 
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
   HistoryLogReader::HistoryLogReader( const Pathname & historyFile,
                                       const ProcessItem & callback )
-    : _pimpl(new HistoryLogReader::Impl(historyFile, callback))
+  : _pimpl(new HistoryLogReader::Impl( historyFile, Options(), ProcessData() ) )
+  { _pimpl->_oldAPICallback = callback; }
+#endif // WITH_DEPRECATED_HISTORYITEM_API
+
+  HistoryLogReader::HistoryLogReader( const Pathname & historyFile_r, const Options & options_r, const ProcessData & callback_r )
+  : _pimpl( new HistoryLogReader::Impl( historyFile_r, options_r, callback_r ) )
   {}
 
   HistoryLogReader::~HistoryLogReader()
   {}
 
-  void HistoryLogReader::setIgnoreInvalidItems(bool ignoreInvalid)
-  { _pimpl->_ignoreInvalid = ignoreInvalid; }
+  void HistoryLogReader::setIgnoreInvalidItems( bool ignoreInvalid_r )
+  { _pimpl->_options.setFlag( IGNORE_INVALID_ITEMS, ignoreInvalid_r ); }
 
   bool HistoryLogReader::ignoreInvalidItems() const
-  { return _pimpl->_ignoreInvalid; }
-
-  void HistoryLogReader::readAll(const ProgressData::ReceiverFnc & progress)
-  { _pimpl->readAll(progress); }
+  { return _pimpl->_options.testFlag( IGNORE_INVALID_ITEMS ); }
 
-  void HistoryLogReader::readFrom(const Date & date,
-      const ProgressData::ReceiverFnc & progress)
-  { _pimpl->readFrom(date, progress); }
+  void HistoryLogReader::readAll( const ProgressData::ReceiverFnc & progress_r )
+  { _pimpl->readAll( progress_r ); }
 
-  void HistoryLogReader::readFromTo(
-      const Date & fromDate, const Date & toDate,
-      const ProgressData::ReceiverFnc & progress)
-  { _pimpl->readFromTo(fromDate, toDate, progress); }
+  void HistoryLogReader::readFrom( const Date & date_r, const ProgressData::ReceiverFnc & progress_r )
+  { _pimpl->readFrom( date_r, progress_r ); }
 
+  void HistoryLogReader::readFromTo( const Date & fromDate_r, const Date & toDate_r, const ProgressData::ReceiverFnc & progress_r )
+  { _pimpl->readFromTo( fromDate_r, toDate_r, progress_r ); }
 
-    /////////////////////////////////////////////////////////////////
   } // namespace parser
   ///////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////
index b8941dd..e174039 100644 (file)
 #define ZYPP_PARSER_HISTORYLOGREADER_H_
 
 #include "zypp/base/PtrTypes.h"
+#include "zypp/base/Flags.h"
 #include "zypp/ProgressData.h"
 #include "zypp/Pathname.h"
 
 #include "zypp/HistoryLogData.h"
 
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+#warning Support for HistoryItem based parsing will be removed in the future.
+#warning Switch to the new HistoryLogData based HistoryLogReader API.
+#endif // WITH_DEPRECATED_HISTORYITEM_API
+
 ///////////////////////////////////////////////////////////////////
 namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
 
   class Date;
 
   ///////////////////////////////////////////////////////////////////
   namespace parser
-  { /////////////////////////////////////////////////////////////////
+  {
 
   ///////////////////////////////////////////////////////////////////
   /// \class HistoryLogReader
@@ -35,42 +41,69 @@ namespace zypp
   /// \ingroup g_ZyppHistory
   /// \ingroup g_ZyppParser
   ///
-  /// Reads a zypp history log file and calls the ProcessItem function
-  /// passed in the constructor for each item read.
+  /// Reads a zypp history log file and calls the \ref ProcessData callback
+  /// passed in the constructor for each valid history line read. The callbacks
+  /// return value indicates whether to continue parsing.
   ///
   /// \code
-  /// struct HistoryItemCollector
-  /// {
-  ///   vector<HistoryItem::Ptr> items;
-  ///
-  ///   bool operator()( const HistoryItem::Ptr & item_ptr )
+  ///   std::vector<HistoryLogData::Ptr> history;
+  ///   parser::HistoryLogReader parser( ZConfig::instance().historyLogFile(),
+  ///                                    HistoryLogReader::Options(),
+  ///     [&history]( HistoryLogData::Ptr ptr )->bool {
+  ///       history.push_back( ptr );
+  ///       return true;
+  ///     } );
+  ///   parser.readAll();
+  ///   ...
+  ///   if ( history[0]->action() == HistoryActionID::INSTALL )
   ///   {
-  ///     items.push_back(item_ptr);
-  ///     return true;
+  ///     // generic access to data fields plain string values:
+  ///     MIL << (*p)[HistoryLogDataInstall::USERDATA_INDEX] << endl;
+  ///
+  ///     // The same maybe more convenient though derived classes:
+  ///     HistoryLogDataInstall::Ptr ip( dynamic_pointer_cast<HistoryLogDataInstall>( p ) );
+  ///     MIL << ip->userdata() << endl;
   ///   }
-  /// }
-  /// ...
-  /// HistoryItemCollector ic;
-  /// HistoryLogReader reader("/var/log/zypp/history", boost::ref(ic));
+  /// \endcode
+  /// \see \ref HistoryLogData for how to access the individual data fields.
   ///
-  /// try
-  /// {
-  ///   reader.readAll();
-  /// }
-  /// catch (const Exception & e)
-  /// {
-  ///   cout << e.asUserHistory() << endl;
-  /// }
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+  /// \note The old API based in HistoryItem instead of HistoryLogData
+  /// is deprecated and may vanish in the future. The new API no longer
+  /// allows direct access to data members, you have to call access methods
+  /// instead.
+  /// \code
+  ///   - // old style
+  ///   - HistoryItem::Ptr ptr;
+  ///   - Date d = ptr->date;
+  ///   + // new style
+  ///   + HistoryLogData::Ptr ptr;
+  ///   + Date d = ptr->date();
   /// \endcode
-  /////////////////////////////////////////////////////////////////////
+#endif // WITH_DEPRECATED_HISTORYITEM_API
+  ///////////////////////////////////////////////////////////////////
   class HistoryLogReader
   {
   public:
-    typedef function< bool( const HistoryItem::Ptr & )> ProcessItem;
+
+    enum OptionBits    ///< Parser option flags
+    {
+      IGNORE_INVALID_ITEMS     = (1 << 0)      ///< ignore invalid items and continue parsing
+    };
+    ZYPP_DECLARE_FLAGS( Options, OptionBits );
 
   public:
-    HistoryLogReader( const Pathname & repo_file,
-                      const ProcessItem & callback );
+    /** Callback type to consume a single history line split into fields.
+     * The return value indicates whether to continue parsing.
+     */
+    typedef function< bool( const HistoryLogData::Ptr & )> ProcessData;
+
+    /** Ctor taking file to parse and data consumer callback.
+     * As \a options_r argument pass \c HistoryLogReader::Options() to
+     * use the default stettings, or an OR'ed combination of \ref OptionBits.
+     */
+    HistoryLogReader( const Pathname & historyFile_r, const Options & options_r, const ProcessData & callback_r );
+
     ~HistoryLogReader();
 
     /**
@@ -78,8 +111,7 @@ namespace zypp
      *
      * \param progress An optional progress data receiver function.
      */
-    void readAll(
-      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+    void readAll( const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
 
     /**
      * Read log from specified \a date.
@@ -89,8 +121,7 @@ namespace zypp
      *
      * \see readFromTo()
      */
-    void readFrom( const Date & date,
-      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+    void readFrom( const Date & date, const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
 
     /**
      * Read log between \a fromDate and \a toDate.
@@ -111,8 +142,7 @@ namespace zypp
      * \param toDate   Date on which to stop reading.
      * \param progress An optional progress data receiver function.
      */
-    void readFromTo( const Date & fromDate, const Date & toDate,
-      const ProgressData::ReceiverFnc &progress = ProgressData::ReceiverFnc() );
+    void readFromTo( const Date & fromDate, const Date & toDate, const ProgressData::ReceiverFnc & progress = ProgressData::ReceiverFnc() );
 
     /**
      * Set the reader to ignore invalid log entries and continue with the rest.
@@ -132,14 +162,26 @@ namespace zypp
     /** Implementation */
     class Impl;
     RW_pointer<Impl,rw_pointer::Scoped<Impl> > _pimpl;
+
+#if defined(WITH_DEPRECATED_HISTORYITEM_API)
+  public:
+    /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class. */
+    typedef function< bool( const HistoryItem::Ptr & )> ProcessItem;
+    /** \deprecated Old unextensible \ref zypp::parser::HistoryLogReader data class.
+     * They grant direct access to data members, so can not be extended
+     * without losing binary compatibility.
+     */
+    HistoryLogReader( const Pathname & repo_file, const ProcessItem & callback ) ZYPP_DEPRECATED;
+#endif // WITH_DEPRECATED_HISTORYITEM_API
   };
-  ///////////////////////////////////////////////////////////////////
 
+  /** \relates HistoryLogReader::Options */
+  ZYPP_DECLARE_OPERATORS_FOR_FLAGS( HistoryLogReader::Options );
 
+  ///////////////////////////////////////////////////////////////////
+
+  } // namespace parser
   /////////////////////////////////////////////////////////////////
-} // namespace parser
-///////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////
 } // namespace zypp
 ///////////////////////////////////////////////////////////////////