CDデータのテンプレート化 - リッチテキスト - 「NotesPeek」をQtでリメイク

前回は、リッチテキストの要素である「CDデータ」を、未定義のものでもバイナリの値を並べて表示できるCdOtherItemクラスと、ライブラリクラスとしてのntlx::cd::Otherクラス、そしてその親クラス(と言うより、インターフェース、純粋仮想関数しか持っていない)となるntlx::Cdクラスを作成しました。定義の手法に関しては、我が座右の書、Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングを参考にさせていただいています。改めて感謝です。

ここで「CDデータ」と呼んでいるものについて簡単におさらいしておきます。

  • Notesデータベースに含まれるもの → Note文書(ドキュメント、設計文書、ACLなど)
  • Note文書に含まれるもの → アイテム(文字列、数値、日時、リッチテキスト、画像、ファイル、設計要素(フィールド、列など))
  • リッチテキストアイテムに含まれるもの → CDデータ(テキスト、段落、画像、表、色、ホットスポットなど)

細かい点は大目に見ていただき、ここで伝えたいのは、CDデータはリッチテキストアイテムの構成要素であるということです。

リファレンスを見ると、「CD」とは「Composite Data」の略であることが分かるので、「データ」が重複しているのは否めませんが、「CD」という接頭辞が一つの大きなグループという位置づけにもなっており、特定の要素を表さない汎用的な名称として、あえて「CDデータ」と呼ばせていただいています。

さて、前回はCdクラスからcd::Otherクラスを派生させました。今回のゴールはCDTEXTというリッチテキストの中のテキスト要素を専用表示させるため、cd::Textクラスを派生させます。ただし、Cdクラスとは別にCDデータが共通して行う処理も多いので、テンプレートクラスcd::Baseを間に挟みます。

// ntlx/cd/base.h

#ifndef NTLX_CD_BASE_H
#define NTLX_CD_BASE_H

#include <ntlx/cd.h>

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

#include <ods.h>

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

namespace ntlx
{
namespace cd
{

template <typename CDxxx, WORD _CDxxx, WORD SIG_CD_xxx>
class NTLXSHARED_EXPORT Base
    : public Cd
{
public:
  Base(char** ppRecord)
  {
    ODSReadMemory(ppRecord, _CDxxx, &record_, 1);
  }

  Base(const Base& other)
    : record_(other.record_)
  {
  }

  Base& operator=(const Base& other)
  {
    if (this != &other)
      record_ = other.record_;
    return *this;
  }

  virtual WORD getSignature() const
  {
    return SIG_CD_xxx;
  }

  virtual WORD odsLength() const
  {
    return record_.Header.Length + record_.Header.Length % 2;
  }

protected:
  mutable CDxxx record_;
};

} // namespace cd

} // namespace ntlx

#endif // NTLX_CD_BASE_H

純粋仮想メソッドCd::toString以外は、テンプレートとした構造体CDxxxを使って表すことができます。CDxxxに当てはまる構造体は300以上もあるので、テンプレートクラスにするこの方法は、理にかなっていてとてもいいと思います。これはほぼそっくり座右の書のアイデアをお借りしています。

  • CDxxx: CDデータの先頭を表す構造体
  • _CDxxx: CDデータシンボル(ODSLength用)
  • SIG_CD_xxx: CDデータシグネチャ(CD構造体先頭用)

これを親として、cd::Textクラスを定義します。

// ntlx/cd/text.h

#ifndef NTLX_CD_TEXT_H
#define NTLX_CD_TEXT_H

#include <ntlx/cd/base.h>

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

#include <editods.h>

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

namespace ntlx
{
namespace cd
{

class NTLXSHARED_EXPORT Text
    : public Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>
{
public:
  Text(char** ppRecord);
  Text(const Text& other);
  Text& operator=(const Text& other);

  QString value() const;

  virtual QString toString() const;

private:
  QString value_;
};

} // namespace cd

} // namespace ntlx

#endif // NTLX_CD_TEXT_H
// cd/text.cpp

#include "ntlx/cd/text.h"

#include "ntlx/lmbcs.h"

namespace ntlx
{
namespace cd
{

Text::Text(char** ppRecord)
  : Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>(ppRecord)
  , value_()
{
  WORD len = record_.Header.Length - ODSLength(_CDTEXT);
  Lmbcs lmbcs(*ppRecord, len);
  value_ = lmbcs.toQString();
  nullToCR(value_);
}

Text::Text(const Text &other)
  : Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>(other)
  , value_(other.value_)
{
}

Text& Text::operator=(const Text& other)
{
  Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>::operator=(other);
  if (this != &other)
    value_ = other.value_;
  return *this;
}

QString Text::value() const
{
  return value_;
}

QString Text::toString() const
{
  return value();
}

} // namespace cd

} // namespace ntlx

cd::Text固有の実装としてはQString value_メンバ変数を持っています。テキストのCDデータは、CDTEXT構造体に続いてLmbcs形式のテキストが収められているので、BaseコンストラクタがCDTEXT構造体分のデータを取得したあとに、Lmbcs文字列をvalue_に、QStringに変換しながら格納しています。Baseコンストラクタが呼び出しているODSReadMemoryは、CDデータ構造体を読み込むと、メモリのポインタをそのサイズ分だけ移動してくれるので便利です。

これをNSFinderに実装してみた例は以下の通りです。

f:id:takahide-kondoh:20170820215659p:plain

前回までの、汎用的なCDデータの出力ではなくて、CDTEXT専用の出力として機能しています。