倭マン's BLOG

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

NamespaceContext の実装を考える (6) : AbstractNamespaceContext

前回までで考えた、javax.xml.namespace.NamespaceContext の実装をサポートする抽象クラスは、結局、次のようになります(一覧):

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.namespace.NamespaceContext;
import static javax.xml.XMLConstants.*;

/** @author waman */
public abstract class AbstractNamespaceContext implements NamespaceContext{
    
    private static final List<String> XML = Collections.singletonList(XML_NS_PREFIX);
    private static final List<String> XMLNS = Collections.singletonList(XMLNS_ATTRIBUTE);

    /** 親ノードの NamespaceContext を返します。 */
    protected abstract NamespaceContext getParentContext();

    /**
     * 第0引数の文字列で指定される接頭辞に対応する名前空間 URI を返します。
     * 祖先要素には遡りません。
     * 該当する名前空間 URI が見つからなかった場合、null を返します。
     */
    protected abstract String getNamespaceURIonCurrentNode(String prefix);

    /**
     * 第0引数で指定される名前空間 URI に対応する接頭辞のリストを返します。
     * 祖先要素には遡りません。
     * 該当する接頭辞が見つからなかった場合は空の List を返します
     * (以前は null を返すとしてましたが間違いです)。
     */
    protected abstract List<String> getPrefixesOnCurrentNode(String uri);

    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;

        // prefix bound on the current node
        String uri = getNamespaceURIonCurrentNode(prefix);
        if(uri != null)
            return uri;

        NamespaceContext parent = getParentContext();
        if(parent != null)
            // prefix bound on a parent node
            return parent.getNamespaceURI(prefix);
        
        // unbound prefix
        return NULL_NS_URI;
    }

    public String getPrefix(String uri) {
        
        validateURI(uri);
        
        if(XML_NS_URI.equals(uri))
            return XML_NS_PREFIX;
        if(XMLNS_ATTRIBUTE_NS_URI.equals(uri))
            return XMLNS_ATTRIBUTE;
        
        List<String> prefixes = getPrefixList(uri);
        
        // unbound namespace uri
        if(prefixes.size() == 0)
            return null;
        
        for(String prefix: prefixes){
            // default namespace uri
            if(prefix.length() == 0)
                return prefix;
        }
        
        //  bound namespace uri
        return prefixes.get(0);
    }

    public Iterator<String> getPrefixes(String uri) {
        
        validateURI(uri);
        
        if(XML_NS_URI.equals(uri))
            return XML.iterator();
        if(XMLNS_ATTRIBUTE.equals(uri))
            return XMLNS.iterator();
        
        return getPrefixList(uri).iterator();
    }

    private List<String> getPrefixList(String uri){

        List<String> parentPrefixes = getParentPrefixList(uri);
        List<String> prefixes = new LinkedList<String>();
        
        for(String prefix: parentPrefixes){
            String u = getNamespaceURIonCurrentNode(prefix);
            if(u == null || u.equals(uri))
                prefixes.add(prefix);
        }
            
        prefixes.addAll(getPrefixesOnCurrentNode(uri));
        
        return prefixes;
    }
    
    private void validateURI(String uri){
        if(uri == null || NULL_NS_URI.equals(uri))
            throw new IllegalArgumentException("URI can't be null or empty String");
    }
    
    private List<String> getParentPrefixList(String uri){
        List<String> prefixes = new LinkedList<String>();
        NamespaceContext parent = getParentContext();
        
        if(parent != null){
            Iterator<?> ite = parent.getPrefixes(uri);
            while(ite.hasNext())
                prefixes.add((String)ite.next());
        }
        
        return prefixes;
    }
}

空白文字列の名前空間 URI の扱いを、以前の記事に載せたメソッドの実装から前回で見た挙動へと変更しています。 また、名前空間 URI や接頭辞の定数を javax.xml.XMLConstants のフィールドで置き換えています。