倭マン's BLOG

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

Jaxen 苦闘 (8) :独自の関数を登録する

前回に続き、今回は独自の関数を登録する方法を見ていきます(記事一覧)。

XPath インターフェースに定義されている FunctionContext アクセッサ


XPath インターフェースには、VariableContext 同様、FunctionContext オブジェクトに対するアクセッサメソッドが定義されています:

package org.jaxen;

public interface XPath {
    ...
    FunctionContext getFunctionContext();
    void setFunctionContext(FunctionContext context);
}

org.jaxen.FuncticonContext インターフェースについては後ほど。

関連する型


独自関数は FunctionContext インターフェースを介して扱います。 自分で FunctionContext インターフェースを実装したクラスを作成することもできなくはありませんが、Jaxen ライブラリに定義されてある XPathFunctionContext クラスを用いるほうが良いでしょう。 理由は後ほど。

  • FunctionContext インターフェース
  • SimpleFunctionContext クラス (implements FunctionContext)
  • XPathFunctionContext クラス (extends SimpleFunctionContext)

FunctionContext インターフェース

FunctionContext インターフェースには、VariableContext インターフェース同様、関数名(名前空間 URI 、接頭辞、ローカル名)を指定して関数オブジェクト (org.jaxen.Function オブジェクト) を取得するメソッドが定義されています:

package org.jaxen;

public interface VariableContext {
    Function getFunction(String uri, String prefix, String localName)
            throws UnresolvableException;
}

FunctionContext#getFunction() に関しても、VariableContext#getVariableValue() と同様の注意事項があります:

  • 関数オブジェクト取得する際に、接頭辞は結果に影響を与えてはいけません。 これはデバッグやエラーハンドリングのために使用します。
  • 接頭辞がない場合は、名前空間 URI に null が指定されます。

Function オブジェクトの詳細は次回。

SimpleFunctionContext クラス

SimpleFunctionContext クラスには FunctionContext インターフェースに定義されているメソッドに加えて、関数名と関数オブジェクトの組を登録するメソッドが定義されています:

package org.jaxen;

public class SimpleFunctionContext implements FunctionContext{

    public SimpleFunctionContext(){...}

    @Override 
    public Function getFunction(String uri, String prefix, String localName)
            throws UnresolvableException{...}

    public void registerFunction(String uri, String localName, Function function) {...}
}

注意をいくつか:

  • 登録する関数は Function オブジェクトなので、関数の評価 (evaluate) がこの関数オブジェクトの状態に依存してしまうと意図しない結果を返す可能性があります。 基本的には状態を持たない (stateless) ようにすべきでしょう。
  • 既に登録されている関数と同名の関数を登録すると、その関数は上書きされます。
  • この SimpleFunctionContext クラスは XPath 1.0 のコア関数ライブラリ (core function library) を返しません。 したがって、独自のコア関数ライブラリを実装するのでなければ、次に見る XPathFunctionContext クラスを使用しましょう。

XPathFunctionContext クラス

XPathFunctionContext クラスは SimpleFunctionContext クラスを継承し、デフォルトで XPathコア関数ライブラリが登録されているクラスです。 新に定義されているインスタンスメソッドはありません:

package org.jaxen;

public class XPathFunctionContext extends SimpleFunctionContext{

    public XPathFunctionContext(){...}
    public XPathFunctionContext(boolean includeExtensionFunctions){...}

    @Override 
    public Function getFunction(String uri, String prefix, String localName)
            throws UnresolvableException{...}

    @Override
    public void registerFunction(String uri, String localName, Function function){...}

    public static FunctionContext getInstance(){...}
}

既に登録されている関数と同名の関数を登録すると、(たとえコア関数ライブラリでも)関数が上書きされます。

boolean を引数にとるコンストラクタは、Jaxen 独自の関数を使用するかどうかを指定します。 引数がないコンストラクタはこのコンストラクタで true を指定したものと同じです。

Jaxen 独自の関数には次の4つが定義されています:

関数 シグネチャ*1 返り値の型 説明
evaluate() evaluate(string) node-set (おそらく)引数の文字列を XPath 式として評価し、結果をノードセットとして返します。
upper-case() upper-case(string) string 引数の文字列を大文字に変換します。
lower-case() lower-case(string) string 引数の文字列を小文字に変換します。
ends-with() ends-with(string, string) boolean 第1引数の文字列が第2引数の文字列で終わるかどうかを返します。

独自関数を登録する


以下では具体的に同時変数を登録するサンプルコードを見ていきます。 (通常そうであろう)名前空間を扱わない場合のみ載せてます。 検索対象となる XML 文書は適当に想像して読んで下さい。

独自関数を登録して使用する手順は以下の通り:

  1. 独自の関数(Function インターフェースの実装クラス)を作成する
  2. XPathFunctionContext オブジェクトを生成する
  3. XPathFunctionContext オブジェクトに関数を登録する
  4. XPath に XPathFunctionContext オブジェクトをセットする

まず独自関数を。 この関数は、単に文字列 "dependency" を返すだけです。

class TargetFunction implements Function{
    @Override
    public String call(Context context, @SuppressWarnings("unchecked") List args){
        return "dependency";
    }
}

で、この関数を登録して使うサンプルコード。 XML 文書の中から "dependency" というローカル名の要素を捜して返します:

        org.dom4j.Document doc = ...  // 検索対象の Document オブジェクト

        XPath xpath = new Dom4jXPath("//*[local-name() = target()]");
        
        // 1. XPathFunctionContext オブジェクトを生成する
        XPathFunctionContext context = new XPathFunctionContext();
        // 2. XPathFunctionContext オブジェクトに関数を登録する
        context.registerFunction(null, "target", new TargetFunction());
        // 3. XPath に XPathFunctionContext オブジェクトをセットする
        xpath.setFunctionContext(context);
        
        @SuppressWarnings("unchecked")
            List<Element> result = xpath.selectNodes(doc);
        
        // 検索結果に対して処理を行う

*1:ここでは「シグネチャ (signature)」に返り値の型を含めていません。 というか、返り値の型は隣に書いてます。 特に深い意味はなく、単に表の見やすさのためだけです。