/*
 * $Id: HTMLCompositeNodeImpl.java,v 1.4 2005/12/17 16:22:27 sugimotokenichi Exp $
 * Copyright (C) 2005 SUGIMOTO Ken-ichi
 * 作成日: 2005/10/29
 */
package feat.v1.impl.template;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import feat.v1.template.CompositeNode;
import feat.v1.template.HTMLElement;
import feat.v1.template.HTMLNode;
import feat.v1.template.HTMLText;
import feat.v1.template.NodeListIterator;
import feat.v1.template.NodeNotFoundException;
import feat.v1.template.NodeSelector;
import feat.v1.template.NodeTreeIterator;

/**
 * HTMLNodeの実装。
 * @author SUGIMOTO Ken-ichi
 */
abstract public class HTMLCompositeNodeImpl extends HTMLNodeImpl implements CompositeNode {

    private static Log log = LogFactory.getLog(HTMLCompositeNodeImpl.class);

    public HTMLNodeImpl firstChild;

    public void setFirstChild(HTMLNode child) {
        this.firstChild = (HTMLNodeImpl)child;
    }

    /**
     * nodeとそれ以降の兄弟ノードをツリーから切り離す。
     * @param node nullのときは何もしない
     * @return 削除したノード。リスト構造は保たれるが親ノードへの参照はなくなっている
     */
    protected HTMLNode detachAll(HTMLNode node) {
        if ( node == null )
            return null;

        // 前のノードとの参照を切る
        try {
            ((HTMLNodeImpl)node.getPrev()).setNext(null);
        }
        catch (NodeNotFoundException ex) {
        }

        // 親ノードからの参照を切る
        try {
            HTMLNodeImpl parentNode = (HTMLNodeImpl)(node.getParent());
            if ( parentNode.getFirstChild() == node ) {
                parentNode.setFirstChild(null);
            }
        }
        catch (NodeNotFoundException ex1) {
        }

        // 親ノードへの参照を切る
        TemplateUtil.setParentAll((HTMLNodeImpl)node, null, null);

        return node;
    }

    // HTMLNodeの実装 ------------------------------------------------------------

    public HTMLNode getFirstChild() throws NodeNotFoundException {
        if ( firstChild == null )
            throw new NodeNotFoundException();

        return firstChild;
    }

    // CompositeNodeの実装 -------------------------------------------------------

    public void addChild(HTMLNode node) {
        if ( node == null )
            return;

        HTMLNodeImpl child = (HTMLNodeImpl)node;

        // 子ノードがなければ最初の子ノードとして追加
        if ( firstChild == null ) {
            firstChild = child;
            child.setPrev(null);
        }
        // 子ノードがある場合は末尾に追加
        else {
            firstChild.getLast().insertAfter(child);
        }

        TemplateUtil.setParentAll(child, null, this);
    }

    public String getString() {
        StringBuffer buf = new StringBuffer();
        NodeListIterator it = new NodeListIterator(firstChild);
        HTMLText textNode;
        while( (textNode = NodeSelector.getText(it)) != null ) {
            buf.append(textNode.getText());
        }
        return buf.toString();
    }

    public void setText(String text) {
        clear();
        if ( text != null )
            addText(text);
    }

    public void addText(String text) {
        if ( text == null )
            return;

        HTMLText textNode;
        // 既存のテキストノードに追加
        try {
            getLastText().addText(text);
        }
        // テキストノードがないときは新しく作る
        catch (NodeNotFoundException ex) {
            textNode = new HTMLTextImpl();
            textNode.setText(text);
            addChild(textNode);
        }
    }

    public void clear() {
        if ( firstChild == null )
            return;

        // 親ノードへの参照を切る
        TemplateUtil.setParentAll((HTMLNodeImpl)firstChild, null, null);

        firstChild = null;
    }

    public HTMLText getText() throws NodeNotFoundException {
        HTMLText ret = NodeSelector.getText(new NodeListIterator(firstChild));
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }

    public HTMLText getText(int n) throws NodeNotFoundException {
        HTMLText ret = NodeSelector.getText(new NodeListIterator(firstChild), n);
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }

    public HTMLText getLastText() throws NodeNotFoundException {
        HTMLText ret = NodeSelector.getLastText(new NodeListIterator(firstChild));
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }

    public HTMLElement getTag(String tagName) throws NodeNotFoundException {
        HTMLElement ret = NodeSelector.getTag(new NodeListIterator(firstChild), tagName);
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }

    public HTMLElement getTag(String tagName, int n) throws NodeNotFoundException {
        HTMLElement ret = NodeSelector.getTag(new NodeListIterator(firstChild), tagName, n);
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }

    public HTMLElement getLastTag(String tagName) throws NodeNotFoundException {
        HTMLElement ret = NodeSelector.getLastTag(new NodeListIterator(firstChild), tagName);
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }

    public int count() {
        int count = 0;
        for(NodeTreeIterator it = new NodeTreeIterator(this); it.hasNext(); it.nextNode()) {
            count++;
        }
        return count;
    }

    /*public int count() {
        return countLocal(this, 0);
    }

    private int countLocal(HTMLNodeImpl node, int count) {
        for( ; node != null; node = node.next) {
            count++;
            try {
                if ( node instanceof HTMLCompositeNodeImpl )
                    count = countLocal((HTMLNodeImpl)node.getFirstChild(), count);
            }
            catch (NodeNotFoundException ex) {
            }
        }
        return count;
    }*/

    /*public HTMLElement findElement(String id) throws NodeNotFoundException {
        for(NodeTreeIterator it = new NodeTreeIterator(this); it.hasNext(); ) {
            HTMLNode node = it.nextNode();
            if ( node instanceof HTMLElement ) {
                String xid = ((HTMLElement)node).getId();
                if ( xid != null && xid.equals(id) ) {
                    return (HTMLElement)node;
                }
            }
        }
        throw new NodeNotFoundException("HTMLCompositeNode.findElement(\""+id+"\")");
    }*/

    public HTMLElement findElement(String id) throws NodeNotFoundException {
        HTMLElement ret = findElementLocal(this, id);
        if ( ret == null )
            throw new NodeNotFoundException("HTMLCompositeNode.findElement(\""+id+"\")");
        return ret;
    }

    private HTMLElement findElementLocal(HTMLNodeImpl element, String id) {
        for(HTMLNodeImpl node=element ; node != null; node = node.next) {
            if ( node instanceof HTMLElement ) {
                String xid = ((HTMLElement)node).getId();
                if ( xid != null && xid.equals(id) ) {
                    return (HTMLElement)node;
                }
            }
            if ( node instanceof HTMLCompositeNodeImpl ) {
                HTMLNodeImpl child = ((HTMLCompositeNodeImpl)node).firstChild;
                if ( child != null ) {
                    HTMLElement ret = findElementLocal(child, id);
                    if ( ret != null )
                        return ret;
                }
            }
        }
        return null;
    }

    public HTMLElement findElementByClass(String className)
            throws NodeNotFoundException {
        HTMLElement ret = NodeSelector.getElementByClass(new NodeTreeIterator(this), className);
        if ( ret == null )
            throw new NodeNotFoundException();
        return ret;
    }
}
