伝説のツール「NotesPeek」をQtでリメイクする(その4・IDテーブル)

IDテーブル

IDテーブルは、Notesにおける文書のためのコンテナの役割を持っています。Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングでも書かれていますが、

文書IDテーブルとは、文書ID(NOTEID)を昇順で保持するコンテナで、std::setとほぼ同じ機能を提供します。

と、あります。もともとAPIはC用に作られているので、C++のコンテナをそのまま使うわけにはいきません。CのみであればこのIDテーブルを使えばいいわけです。またC++であればstd::setを使ってみても問題ありません。ただ、著者の津田氏も勧めているように、IDテーブルの仕組みの理解と、C++のような実装をする勉強のためにも、IDテーブルを取り込んだ、C++っぽいクラスを作ってみます。津田氏の場合、NoteIdCollectionクラスとそのイテレータクラスで構成されています。9割以上は氏のコーディングを参考にしています。ただ、あまりNoteクラスと密な関係にせず、基本的にはNOTEIDの出し入れというシンプルな構造に変えてみました。

まず、IDテーブルクラスの実装を紹介する前に、ここで扱うIDテーブル関連のAPIを紹介します。

IDテーブルは、IDCreateTable関数によって生成し、IDDestroyTable関数によって破棄します(例外もあります)。生成されたIDテーブルはDHANDLEにて管理します。

#include <idtable.h>

STATUS LNPUBLIC IDCreateTable (DWORD Alignment, DHANDLE far *rethTable);
STATUS LNPUBLIC IDDestroyTable(DHANDLE hTable);

ここで、Alignmentはsizeof(NOTEID)を渡すことになります。

IDテーブルにIDを追加するには、IDInsert関数を使います。

#include <idtable.h>

STATUS  LNPUBLIC IDInsert (DHANDLE hTable, DWORD id, BOOL far *retfInserted);

戻り値はいつものステータス値ですが、IDが挿入できたかどうかは3番目の引数、retfInsertedを見ます。これは、IDがすでにIDテーブル内に存在していればfalseを返します。必要なければnullptrを与えておきます。

テーブル内のIDは通常IDDeleteかIDDeleteAllを使って削除しますが、その実装は後日とします。また、Noteクラスとの連携に唯一getNoteメソッドを実装していますが、これも後日説明します。

それでは、僕流のIDTableクラスと、IDTable::iteratorクラスです。

// ntlx/idtable.h

#ifndef NTLX_IDTABLE_H
#define NTLX_IDTABLE_H

#include "ntlx_global.h"
#include "ntlx/status.h"

#if defined(NT)
#pragma pack(push, 1)
#endif

#include <nsfdata.h>

#if defined(NT)
#pragma pack(pop)
#endif

namespace ntlx
{

class Database;
class Note;

/**
 * @brief IDテーブルクラス
 */
class NTLXSHARED_EXPORT IDTable
    : public IStatus
{
public:

  /**
   * @brief イテレータインナークラス
   */
  class NTLXSHARED_EXPORT iterator
  {
  public:
    /**
     * @brief デフォルトコンストラクタ
     */
    iterator();

    /**
     * @brief NOTEIDを返す演算子
     * @return NOTEID
     */
    NOTEID operator*();

    /**
     * @brief 前置きインクリメント演算子
     * @return 自身への参照
     */
    iterator& operator++();

    /**
     * @brief 後置きインクリメント演算子
     * @return インクリメントする前のイテレータ
     */
    iterator operator++(int);

    /**
     * @brief NOTEIDに対応したNoteオブジェクトを取得する
     * @param db 取得元になるデータベースオブジェクト
     * @return Noteオブジェクト
     */
    Note getNote(Database& db) const;

  protected:
    /**
     * @brief コンストラクタ
     * @param pIdTable IDTableオブジェクトへのポインタ
     * @param noteId NOTEID
     * @param isLast 最後を示しているか
     */
    iterator(IDTable* pIdTable, NOTEID noteId, bool isLast = false);

    IDTable* idTable_;
    NOTEID id_;
    bool isLast_;

    /**
     * @brief 等値演算子
     * @param lhs 左辺値
     * @param rhs 右辺値
     * @return ブール値
     */
    friend NTLXSHARED_EXPORT bool operator==(
        const iterator& lhs
        , const iterator& rhs
        );

    /**
     * @brief 不等値演算子
     * @param lhs 左辺値
     * @param rhs 右辺値
     * @return ブール値
     */
    friend NTLXSHARED_EXPORT bool operator!=(
        const iterator& lhs
        , const iterator& rhs
        );

    friend class IDTable;
  };

  /**
   * @brief コンストラクタ
   */
  IDTable();

  /**
   * @brief デストラクタ
   */
  virtual ~IDTable();

  /**
   * @brief 最初のイテレータを返す
   * @return 最初のイテレータ
   */
  iterator begin();

  /**
   * @brief 最後のイテレータを返す
   * @return 最後のイテレータ
   */
  iterator end();

  /**
   * @brief NOTEIDを挿入する
   * @param id NOTEID
   * @return 重複していなければ真
   */
  bool insert(NOTEID id);

private:
  DHANDLE handle_;

  friend class iterator;
};

} // namespace ntlx

#endif // IDTABLE_H
// idtable.cpp

#include "ntlx/idtable.h"
#include "ntlx/note.h"

#if defined(NT)
#pragma pack(push, 1)
#endif

#include <idtable.h>

#if defined(NT)
#pragma pack(pop)
#endif

namespace ntlx
{

IDTable::iterator::iterator()
  : idTable_(nullptr)
  , id_(0)
  , isLast_(false)
{
}

NOTEID IDTable::iterator::operator *()
{
  return id_;
}

IDTable::iterator& IDTable::iterator::operator++()
{
  isLast_ = !IDScan(idTable_->handle_, id_ == 0, &id_);
  return *this;
}

IDTable::iterator IDTable::iterator::operator++(int)
{
  iterator it = *this;
  operator++();
  return it;
}

Note IDTable::iterator::getNote(Database &db) const
{
  return Note(db, id_);
}

IDTable::iterator::iterator(IDTable *pIdTable, NOTEID noteId, bool isLast)
  : idTable_(pIdTable)
  , id_(noteId)
  , isLast_(isLast)
{
}

bool operator==(const IDTable::iterator& lhs, const IDTable::iterator& rhs)
{
  if (lhs.isLast_ && rhs.isLast_)
    return true;
  else
    return lhs.id_ == rhs.id_;
}

bool operator!=(const IDTable::iterator& lhs, const IDTable::iterator& rhs)
{
  return !operator==(lhs, rhs);
}

IDTable::IDTable()
  : IStatus()
  , handle_(NULLHANDLE)
{
  lastStatus_ = IDCreateTable(sizeof(NOTEID), &handle_);
}

IDTable::~IDTable()
{
  if (handle_)
    IDDestroyTable(handle_);
}

IDTable::iterator IDTable::begin()
{
  if (handle_ == NULLHANDLE)
    return end();
  return ++iterator(this, 0);
}

IDTable::iterator IDTable::end()
{
  return iterator(this, 0, true);
}

bool IDTable::insert(NOTEID id)
{
  Q_ASSERT(handle_);

  BOOL b = false;
  lastStatus_ = IDInsert(handle_, id, &b);
  return b;
}

} // namespace ntlx