伝説のツール「NotesPeek」をQtでリメイクする(その13・名前)

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

Notesが扱うデータの基本型は、テキスト、数値、日時の3つとその複数値です。それ以外については、おおよそ次の3つに分けられます。1つはリッチテキスト、2つめがバイナリ形式、そして3つめが基本型に応用になります。2つめのバイナリ形式は、画像や添付ファイル、@式などがそれにあたります。そして3つめの「基本型の応用」とは、基本型に書式や属性によって意味を持たせたものを指します。

例えば今回のお題「名前」は、「型」という意味では単なる「テキスト」です。しかし、「CN=Taro Yamada/OU=Sales/O=Acme」という「書式」を持つことで「名前型」というデータになります。

Notes APIにおいて、「名前型」はDNという接頭辞で始まる関数を使います。Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングによると、「Domain Name」(領域名)の略だとあります。一方、APIのリファレンスには「Distinguished Name」(識別名)のように書かれています。訳語からいっても後者ではないかと思います。ただ、残念なことに今回の実装には「Domain Name」の方でクラスを構築してしまったので、次のバージョンでは修正したいと思っています(少々綴りが長いですが)。

識別名クラスには、1つのメンバ変数(Lmbcs型)を保持するシンプルクラスです。ルールとして、このメンバ変数に保存する識別名は常に「基準書式(Canonical)」であることとします。

最初にヘッダーファイルの実装例をご覧ください。

// ntlx/domainname.h

#ifndef NTLX_DOMAINNAME_H
#define NTLX_DOMAINNAME_H

#include <ntlx/lmbcs.h>

namespace ntlx
{

/**
 * @brief ドメイン名(基準書式名)クラス
 */
class NTLXSHARED_EXPORT DomainName
    : public IStatus
{
public:
  /**
   * @brief デフォルトコンストラクタ
   */
  DomainName();

  /**
   * @brief コンストラクタ(LMBCS文字列から)
   * @param name 書式化する名前文字列(LMBCS)
   */
  DomainName(const Lmbcs& name);

  /**
   * @brief コンストラクタ(QString文字列から)
   * @param name 書式化する名前文字列(QString)
   */
  DomainName(const QString& name);

  /**
   * @brief コピーコンストラクタ
   * @param other コピー元
   */
  DomainName(const DomainName& other);

  /**
   * @brief 代入演算子
   * @param other 代入元
   * @return 自身への参照
   */
  DomainName& operator=(const DomainName& other);

  /**
   * @brief 文字列が空なら真
   * @return 真/偽
   */
  bool isEmpty() const { return name_.isEmpty(); }

  /**
   * @brief 基準書式をLMBCSで返す
   * @return 基準書式LMBCS
   */
  Lmbcs canonical() const { return name_; }

  /**
   * @brief 省略書式をLMBCSで返す
   * @return 省略書式LMBCS
   */
  Lmbcs abbreviated() const;

  /**
   * @brief 代入演算子
   * @param name 代入元LMBCS
   * @return
   */
  DomainName& operator=(const Lmbcs& name);

  /**
   * @brief 代入演算子
   * @param name 代入元QString
   * @return
   */
  DomainName& operator=(const QString& name);

  /**
   * @brief 等価演算子
   * @param lhs 左辺
   * @param rhs 右辺
   * @return 等価なら真
   */
  friend bool operator==(const DomainName& lhs, const DomainName& rhs);

  /**
   * @brief 不等価演算子
   * @param lhs 左辺
   * @param rhs 右辺
   * @return 不透過なら真
   */
  friend bool operator!=(const DomainName& lhs, const DomainName& rhs);

protected:
  void canonicalize(const Lmbcs& name);

private:
  Lmbcs name_;
};

} // namespace ntlx

#endif // NTLX_DOMAINNAME_H

次に、ソースファイルの実装例です。

// domainname.cpp

#include "ntlx/domainname.h"

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

#include <dname.h>
#include <names.h>

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

namespace ntlx
{

DomainName::DomainName()
  : IStatus()
  , name_()
{
}

DomainName::DomainName(const Lmbcs& name)
  : IStatus()
  , name_()
{
  canonicalize(name);
}

DomainName::DomainName(const QString& name)
  : IStatus()
  , name_()
{
  canonicalize(Lmbcs::fromQString(name));
}

DomainName::DomainName(const DomainName &other)
  : IStatus()
  , name_(other.name_)
{
}

DomainName& DomainName::operator=(const DomainName& other)
{
  if (this == &other) return *this;
  name_ = other.name_;
  return *this;
}

Lmbcs DomainName::abbreviated() const
{
  char abbreviate[MAXUSERNAME];
  WORD len;
  lastStatus_ = DNAbbreviate(
        0L
        , nullptr
        , name_.constData()
        , abbreviate
        , MAXUSERNAME
        , &len
        );
  if (lastStatus().success())
    return Lmbcs(abbreviate, len);
  return Lmbcs();
}

void DomainName::canonicalize(const Lmbcs& name)
{
  char buffer[MAXUSERNAME];
  WORD len;
  lastStatus_ = DNCanonicalize(
        0L
        , nullptr
        , name.constData()
        , buffer
        , MAXUSERNAME
        , &len
        );
  if (lastStatus().success())
    name_ = Lmbcs(buffer, len);
  else
    name_ = Lmbcs();
}

DomainName& DomainName::operator=(const Lmbcs& name)
{
  canonicalize(name);
  return *this;
}

DomainName& DomainName::operator=(const QString& name)
{
  canonicalize(Lmbcs::fromQString(name));
  return *this;
}

bool operator==(const DomainName& lhs, const DomainName& rhs)
{
  return lhs.name_ == rhs.name_;
}

bool operator!=(const DomainName& lhs, const DomainName& rhs)
{
  return !operator==(lhs, rhs);
}

} // namespace ntlx

ポイントは、abbreviatdメソッドとcanonicalizeメソッドでしょう。

まず、canonicalizeメソッドはLMBCS文字列を受け取って、それを基準書式に変換して内部のメンバ変数に保持します。

次にabbreviatedメソッドは、内部のメンバ変数を省略書式にしてLmbcsとして返します。

他にも識別名には関数が存在しますが、今回はここまでとします。