倭マン's BLOG

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

改・NamespaceContext の実装を考える (2) : AbstractNamespaceContext

今回は AbstractNamespaceContext の実装を考えます(一覧)。

AbstractNamespace は NamespaceContext の JavaDoc で規定された、「特別な引数」に対して「特別な返り値」を返す、という処理を行わせます。

getNamespaceURI(String) メソッドの実装


まず、AbstractNamespaceContext 以下の抽象メソッドを定義します:

protected abstract String getNamespaceURISimply(String uri);

このメソッドは、getNamespaceContext() メソッドと同様に、引数の接頭辞に対応する名前空間 URI を返します。  ただし、 固定された対応(null と接頭辞 "xml", "xmlns" に対するもの)を考慮する必要はありません(Simply)。

このメソッドを用いて getNamespaceURI(String) を以下のように実装します:

    public String getNamespaceURI(String prefix) {
        if(prefix == null) 
            throw new IllegalArgumentException("Prefix can't be null");
        if(XML_NS_PREFIX.equals(prefix))  
            return XML_NS_URI;
        if(XMLNS_ATTRIBUTE.equals(prefix)) 
            return XMLNS_ATTRIBUTE_NS_URI;
        
        return getNamespaceURISimply(prefix);
    }

getPrefix(String), getPrefixes(String) メソッドの実装


基本的には、これらも getNamespaceURI(String) メソッドと同様に、getXxxxSimply() メソッドを定義します。 ただし、getPrefixes(String) の返り値が Iterator で扱いにくいので、接頭辞のセット (Set) を返すメソッド getPrefixSet(String) を定義し*1、それに Simply メソッド getPrefixSetSimply(String) を定義します:

    /**
     * 引数の名前空間 URI に対応する接頭辞のセットを返します。
     * このメソッドの返り値の Set<String> に対して iterator() を呼び出すと、
     * NamespaceContext#getPrefixes(String)
     * で規定されたイテレータが返されなければなりません。 
     */
    private Set<String> getPrefixSet(String uri) {
        if(XML_NS_URI.equals(uri))
            return Collections.singleton(XML_NS_PREFIX);
        if(XMLNS_ATTRIBUTE.equals(uri))
            return Collections.singleton(XMLNS_ATTRIBUTE);
        
        return getPrefixSetSimply(uri);
    }

    protected abstract Set<String> getPrefixSetSimply(String uri);

この getPrefixSet(String) メソッドを用いて getPrefix(String), getPrefixes(String) を実装するのは以前の記事と同じなので省略。

定義された抽象メソッド


結局、定義された抽象メソッドは以下の2つです:

    /**
     * 引数の接頭辞に対応する名前空間 URI を返します。 
     * ただし、 固定された対応(null と接頭辞 "xml", "xmlns" に対するもの)は
     * 考慮する必要はありません。
     */
    protected abstract String getNamespaceURISimply(String prefix);
    
    /**
     * 引数の名前空間 URI に対応する接頭辞のセットを返します。
     * ただし、固定された対応(null と名前空間 URI "http://www.w3.org/XML/1998/namespace",
     *  "http://www.w3.org/2000/xmlns/")は考慮する必要はありません。
     */
    protected abstract Set<String> getPrefixSetSimply(String uri);

Java コード


AbstractNamespaceContext の Java コードは以下のようになります(上記のコードから一部修正):

import java.util.*;
import javax.xml.namespace.NamespaceContext;
import static javax.xml.XMLConstants.*;

/** @author waman */
public abstract class AbstractNamespaceContext
        implements NamespaceContext{

    public String getNamespaceURI(String prefix) {
        if(prefix == null) 
            throw new IllegalArgumentException("Prefix can't be null");
        if(XML_NS_PREFIX.equals(prefix))  
            return XML_NS_URI;
        if(XMLNS_ATTRIBUTE.equals(prefix)) 
            return XMLNS_ATTRIBUTE_NS_URI;
        
        return getNamespaceURISimply(prefix);
    }
    
    /**
     * 引数の接頭辞に対応する名前空間 URI を返します。 
     * ただし、 固定された対応(null と接頭辞 "xml", "xmlns" に対するもの)は
     * 考慮する必要はありません。
     */
    protected abstract String getNamespaceURISimply(String prefix);

    public String getPrefix(String uri) {
        
        Set<String> prefixes = getPrefixSet(uri);
        
        // unbound namespace uri
        if(prefixes.size() == 0)
            return null;
        
        // default namespace uri
        if(prefixes.contains(DEFAULT_NS_PREFIX))
            return DEFAULT_NS_PREFIX;
        
        //  bound namespace uri
        for(String prefix: prefixes)
            return prefix;
        
        throw new IllegalArgumentException();
    }
    
    public Iterator<String> getPrefixes(String uri){
        return getPrefixSet(uri).iterator();
    }

    /**
     * 引数の名前空間 URI に対応する接頭辞のセットを返します。
     * このメソッドの返り値の Set&lt;String> に対して iterator() を呼び出すと、
     * NamespaceContext#getPrefixes(String)
     * で規定されたイテレータが返されなければなりません。 
     */
    private Set<String> getPrefixSet(String uri) {
        
        validateURI(uri);
        
        if(XML_NS_URI.equals(uri))
            return Collections.singleton(XML_NS_PREFIX);
        if(XMLNS_ATTRIBUTE.equals(uri))
            return Collections.singleton(XMLNS_ATTRIBUTE);
        
        return getPrefixSetSimply(uri);
    }

    private void validateURI(String uri){
        if(uri == null || NULL_NS_URI.equals(uri))
            throw new IllegalArgumentException("URI can't be null or empty String");
    }
    
    /**
     * 引数の名前空間 URI に対応する接頭辞のセットを返します。
     * ただし、固定された対応(null と名前空間 URI "http://www.w3.org/XML/1998/namespace",
     *  "http://www.w3.org/2000/xmlns/")は考慮する必要はありません。
     */
    protected abstract Set<String> getPrefixSetSimply(String uri);
}

*1:これは以前の記事で getPrefixList(String) としていたものです。 List を Set に変えたのは、あまり本質的な意味はありません。 一応、重複を許さないようにしているだけです。