倭マン's BLOG

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

続・NamespaceContext の実装を考える

前回までは、DOM や dom4j のようにメモリ上に XML ツリーを展開して扱う API のための NamespaceContext 実装を考えていました(一覧)。

今回はそれを利用して、StAX のような API で用いることのできる NamespaceContext 実装を考えてみます。

方針


実装方針としては、java.util.Map を用いて接頭辞と名前空間 URI を登録できるようにします。 ただし純粋に Map を用いた実装はこちらにあるので*1、ベースとなる NamespaceContext が使えるようにしましょう。 これによって、各要素で一から接頭辞と名前空間 URI のペアを登録する必要が無くなります。

実装クラスの名前は MapNamespaceContext とします。

クラス定義とフィールド


まずはクラス定義とフィールドから。

public class MapNamespaceContext extends AbstractNamespaceContext {
    
    private final NamespaceContext baseContext;
    private final Map<String, String> context;
}

クラスは 前回までで作成した AbstractNamespaceContext を継承します。 フィールドは、ベースとなる NamespaceContext オブジェクトとそれに付け加える接頭辞、名前空間 URI のマップを保持する java.util.Map オブジェクトが必要となります。

コンストラクタ


ベースとなる NamespaceContext がなくても MapNamespaceContext が使えるように、コンストラクタは以下の2つを定義します:

public MapNamespaceContext()
public MapNamespaceContext(NamespaceContext baseContext)

実装には、何もしない NamespaceContext オブジェクト (NULL_CONTEXT) を用います(この NULL_CONTEXT の実装は間違ってます。 正しくは後日。):

    public MapNamespaceContext(){
        this(NULL_CONTEXT);
    }
    
    public MapNamespaceContext(NamespaceContext baseContext){
        this.baseContext = baseContext;
        this.context = new HashMap<String, String>();
    }

    private static NamespaceContext NULL_CONTEXT = new NamespaceContext(){
        
        public String getNamespaceURI(String prefix) {
            return null;
        }

        public String getPrefix(String namespaceURI) {
            return null;
        }

        public Iterator<String> getPrefixes(String namespaceURI) {
            return Collections.<String>emptyList().iterator();
        }
    };
}

接頭辞・名前空間 URI を登録するメソッド


次は接頭辞と名前空間 URI の組を登録するメソッドです。 これらは java.util.Map 風に。 登録を取り消すメソッドは定義していません:

    public void put(String prefix, String uri){
        this.context.put(prefix, uri);
    }
    
    public void putAll(Map<String, String> map){
        this.context.putAll(map);
    }

AbstractNamespaceContext の抽象メソッド


最期は AbstractNamespaceContext に定義されている抽象メソッドの実装です:

    @Override
    protected NamespaceContext getParentContext() {
        return this.baseContext;
    }

    @Override
    protected String getNamespaceURIonCurrentNode(String prefix) {
        if(this.context.containsKey(prefix))
            return this.context.get(prefix);
        else
            return null;  // ※当初 NULL_NS_URI にしてましたが間違いです。;
    }

    @Override
    protected List<String> getPrefixesOnCurrentNode(String uri) {
        List<String> list = new LinkedList<String>();
        
        for(Entry<String, String> entry: this.context.entrySet()){
            if(entry.getValue().equals(uri))
                list.add(entry.getKey());
        }
        
        return list;
    }

コレで完了。

まとめ


全てをまとめると、結局次のようになります:

public class MapNamespaceContext extends AbstractNamespaceContext {
    
    private final NamespaceContext baseContext;
    private final Map<String, String> context;
    
    public MapNamespaceContext(){
        this(NULL_CONTEXT);
    }
    
    public MapNamespaceContext(NamespaceContext baseContext){
        this.baseContext = baseContext;
        this.context = new HashMap<String, String>();
    }
    
    public void put(String prefix, String uri){
        this.context.put(prefix, uri);
    }
    
    public void putAll(Map<String, String> map){
        this.context.putAll(map);
    }

    @Override
    protected NamespaceContext getParentContext() {
        return this.baseContext;
    }

    @Override
    protected String getNamespaceURIonCurrentNode(String prefix) {
        if(this.context.containsKey(prefix))
            return this.context.get(prefix);
        else
            return null;  // ※当初 NULL_NS_URI にしてましたが間違いです。;
    }

    @Override
    protected List<String> getPrefixesOnCurrentNode(String uri) {
        List<String> list = new LinkedList<String>();
        
        for(Entry<String, String> entry: this.context.entrySet()){
            if(entry.getValue().equals(uri))
                list.add(entry.getKey());
        }
        
        return list;
    }
    
    private static NamespaceContext NULL_CONTEXT = new NamespaceContext(){
        
        public String getNamespaceURI(String prefix) {
            return null;
        }

        public String getPrefix(String namespaceURI) {
            return null;
        }

        public Iterator<String> getPrefixes(String namespaceURI) {
            return Collections.<String>emptyList().iterator();
        }
    };
}

*1:あちらの実装では、getPrefix() メソッドにデフォルト名前空間URI を渡したときに空文字列が返されない場合がありそうですが。