倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Jaxen 苦闘 (9) :式を評価するコンテキストと Context クラス

今回は、式を評価するのに必要な Context クラスとそれに関連するクラス、インターフェースを見ていきます(記事一覧):

  • Context クラス
  • ContextSupport クラス
  • NamespaceContext クラス
  • FunctionContext クラス
  • VariableContext クラス
  • Navigator クラス

Context クラスは、Function インターフェースの call() メソッドのパラメータに使われていて、独自関数を作成する際にこのパラメータからコンテキスト内容を取得することができます。

Context クラス


XPath で関数を評価する際には、次の4つからなるコンテキストが必要なのでした(XPath 1.0 のコア関数 (1) : ノードセット関数 (Node Set Functions)の最初参照。 Jaxen の API に合わせるため、少し順序を変えています。):

  • コンテキスト・ノード
  • コンテキスト位置とコンテキスト・サイズ
  • 名前空間宣言のセット
  • 関数ライブラリ
  • 変数のセット

Function#call() メソッドのパラメータにある Context クラスは、これらの情報にアクセスするメソッドを持っています*1

class Context extends Object implements Serializable{

    // コンストラクタ
    Context(ContextSupport contextSupport) 

    // コンテキスト・ノード
    List getNodeSet()
    void setNodeSet(List nodeSet) 

    //コンテキスト位置とコンテキスト・サイズ
    int getPosition()
    void setPosition(int position)
    int getSize()
    void setSize(int size)

    // 名前空間 URI を取得する
    String translateNamespacePrefixToUri(String prefix)

    // 関数を取得する
    Function getFunction(String namespaceURI, String prefix, String localName)

    // 変数の値を取得する
    Object getVariableValue(String namespaceURI, String prefix, String localName)

    Navigator getNavigator() 
    ContextSupport getContextSupport()
    void setContextSupport(ContextSupport contextSupport)
    Context duplicate() 
}
  • ContextSupport クラスは次に見ます
  • Navigator インターフェースはノード間を移動するために使用します
  • duplicate() メソッドはこのコンテキストの(浅い)コピーを作成します

ContextSupport クラス


ContextSupport クラスは、先ほどの Context クラスに保持されて、コンテキスト内容の解決(名前空間 URI、関数、変数など)をサポートするクラスです。 具体的には

  • NamespaceContext
  • FunctionContext
  • VariableContext
  • Navigator

インスタンスを保持して、それぞれのインターフェースに定義されたコンテキスト内容解決のメソッド(後で見ます)を呼び出します:

class ContextSupport extends Object implements Serializable{

    // コンストラクタ
    ContextSupport() 
    ContextSupport(
        NamespaceContext namespaceContext,
        FunctionContext functionContext,
        VariableContextContext variableContext, 
        Navigator navigator) 

    // 名前空間のコンテキスト
    NamespaceContext getNamespaceContext()
    void setNamespaceContext(NamespaceContext namespaceContext)
    String translateNamespacePrefixToUri(String prefix)    // 名前空間 URI の解決

    // 関数ライブラリのコンテキスト
    FunctionContext getFunctionContext()
    void setFunctionContext(FunctionContext functionContext)
    Function getFunction(String namespaceURI, String prefix, String localName)    // 関数の解決

    // 変数のコンテキスト
    VariableContext getVariableContext()
    void setVariableContext(VariableContext variableContext)
    Object getVariableValue(String namespaceURI, String prefix, String localName)    // 変数の解決

    // ナビゲータ
    Navigator getNavigator()
}

NamespaceContext インターフェース


名前空間のコンテキストを表すインターフェースです。 与えられた接頭辞から名前空間 URI を取得するメソッドが定義されています。

interface NamespaceContext{
    String translateNamespacePrefixToUri(String prefix)
}

このインターフェースは javax.xml.namespace.NamespaceContext と同じような役割を担いますが、次のような点が異なります

  • 名前空間 URI から接頭辞を取得するメソッドは定義されていない(XPath 式の評価には必要ない)
  • XPath では「デフォルト名前空間」がないため、引数の接頭辞が空文字列なら null を返す
  • 接頭辞が "xml" なら、名前空間 URI "http://www.w3.org/XML/1998/namespace" を返す
  • 該当する接頭辞が見つからなければ null を返す

FunctionContext インターフェース


関数ライブラリのコンテキストを表すインターフェースです。 与えられた名前空間 URI と関数名から、該当する関数を取得します。 接頭辞はデバッグの為に定義されているので、関数の解決にはこれを使用してはいけません。

interface FunctionContext{
    Function getFunction(String namespaceURI, String prefix, String localName) 
}

VariableContext インターフェース


変数のコンテキストを表すインターフェースです。 与えられた名前空間 URI と関数名から、該当する変数の値を取得します。 接頭辞はデバッグの為に定義されているので、関数の解決にはこれを使用してはいけません。
変数のコンテキスト

interface VariableContext{
    Object getVariableValue(String namespaceURI, String prefix, String localName) 
}

Navigator


Navigator はノード間の移動を(オブジェクトモデルによらず)行うインターフェースです。 独自のオブジェクトモデルを扱う際に、ほぼ唯一実装しなければならない型です。 逆に言えば、オブジェクトモデルに依存する部分を全て押し込んだ型とも言えます。 ただ、メソッド数が多いので今回は省略。 詳しくは次回(があれば)。
入門XML

入門XML

*1:Jaxen の Function 型と異なり、javax.xml.xpath パッケージにある XPathFunction はこういったコンテキストにアクセスする方法が与えられていません。 これらのコンテキストは XPath のコア関数を実装するためにも必要なはずなのですが。 javax.xml.xpath パッケージでは果たしてどうやってコア関数を実現しているのか疑問。