package org.itscool.commons.bean;


import java.io.IOException;
import org.itscool.commons.collection.KeyLinkedList;

import org.itscool.commons.document.XmlConfigReader;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Bean定義ファイル(di-config.xml)を解析するクラスです<br/>
 * Bean定義ファイルは以下の書式で作成してください<br/>
 *
 * Bean定義ファイルの記述例<hr>
 * <pre>
 * &lt;?xml version="1.0" encoding="Shift_JIS" ?&gt;
 * &lt;di-config&gt;
 *     &lt;bean-mappings&gt;
 *         &lt;bean id="sample" type="SampleBeanImpl"&gt;
 *             &lt;property name="id" value="1"/&gt;
 *             &lt;property name="name" value="ios taro"/&gt;
 *             &lt;property name="age" value="32"/&gt;
 *             &lt;property name="child" ref="child"/&gt;
 *         &lt;/bean&gt;
 *         &lt;bean&gt;
 *             &lt;bean id="child" type="SampleChild"&gt;
 *             &lt;property name="name" value="ios Jr"/&gt;
 *         &lt;/bean&gt;
 *         &lt;bean id="params" type="SampleParams" singleton="true"&gt;
 *         &lt;property name="infos" collection="map"&gt;
 *             &lt;bean id="encoding" type="java.lang.String" value="Shift_JIS"/&gt;
 *             &lt;bean id="weber-config" value="weber-config.xml"/&gt;
 *             &lt;bean id="stylist-config" value="stylist-config.xml"/&gt;
 *             &lt;bean id="message-config" value="message-config.xml"/&gt;
 *         &lt;/property&gt;
 *         &lt;/bean&gt;
 *     &lt;/bean-mappings&gt;
 * &lt;/di-config&gt;
 * </pre>
 *
 * <p>
 * タグの説明<hr><br/>
 * &lt;di-config&gt; ：必須項目です（Bean定義ファイルに1つだけ定義)<br/>
 * &lt;bean-mappings&gt;：必須項目です（Bean定義ファイルに1つだけ定義）<br/>
 * &lt;bean&gt;JavaBeans情報を定義します<br/>
 * &lt;bean&gt;タグは更に以下の属性を指定します<br/>
 * 　id：JavaBeansの論理名称<BR>
 * 　name：JavaBeansの物理名称（パッケージ名を含めたクラス名）<br/>
 * 　singleton：シングルトンクラスの場合はtrueを指定してください<br/>
 * </p>
 * <p>
 * &lt;property&gt;：JavaBeansのプロパティ情報を定義します<br/>
 * &lt;property&gt;タグは更に以下の属性を指定します<br/>
 * 　name：属性の名前（JavaBeansではこの属性名に対応したアクセッサメソッドが必要です）<br/>
 * 　value：プロパティの初期値<br/>
 * 　ref：valueの代わりにrefを指定することもできます。<br/>
 * 　refは、Bean定義ファイル内に定義された別のJavaBeansを論理名称で指定することができます。<br/>
 *
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1 新規作成
 * @version 1.10A 2006/01/17 配列保持対応
 */
public class BeanConfigReader extends XmlConfigReader{
    /**
     * &lt;bean-mappings&gt;要素を解析します<br>
     * 解析結果はhashMapにセットします<br>
     * @param hashMap XMLの解析結果
     */
    protected void doCreate(KeyLinkedList hashMap) throws IOException{
        
        /* ルートタグ名を取得 */
        Element root = document.getDocumentElement();
        if( root == null ){
            String err = "Can not root element at di-config.xml(" +
                getClass().getName()+")";
            throw new IOException(err);
        }
        
        /* action-mappings の取得*/
        NodeList mappingsList = root.getElementsByTagName("bean-mappings");
        if( mappingsList == null ){
            String err = "Can not element at <bean-maggpins>(" +
                getClass().getName()+")";
            throw new IOException(err);
        }
        
        Node mappings = mappingsList.item(0);
        if( mappings != null ){
            createMappings(mappings, hashMap);
        }
    }
    
    /**
     * &lt;bean-mappings&gt;要素を解析します
     * @param mappings &lt;bean-mappings&gt;要素
     * @param hashMap 解析後の&lt;bean&gt;情報を追加するノードリスト
     * @throws Exception
     */
    protected void createMappings(Node mappings, KeyLinkedList hashMap)
    throws IOException{
        NodeList mappingsChilds = mappings.getChildNodes();
        
        for( int i = 0; i < mappingsChilds.getLength(); i++) {
            Node beanInfoNode = mappingsChilds.item(i);
            if( beanInfoNode.getNodeName().equals("bean")){
                createBeanInfo(beanInfoNode, hashMap);
            }
        }
    }
    
    /**
     * &lt;beanInfoNode-mappings&gt;要素の属性と子要素を解析します
     * @param beanInfoNode &lt;beanInfoNode&gt;要素
     * @param hashMap 解析後の&lt;beanInfoNode&gt;情報を追加するノードリスト
     * @throws Exception
     */
    protected void createBeanInfo(Node beanInfoNode, KeyLinkedList hashMap)
    throws IOException{
        NamedNodeMap attributes = beanInfoNode.getAttributes();
        Node id = attributes.getNamedItem("id");
        Node type = attributes.getNamedItem("type");
        Node singleton = attributes.getNamedItem("singleton");
        Node value = attributes.getNamedItem("value");
        Node collection = attributes.getNamedItem("collection");
        
        if( id == null || id.getNodeName().length() == 0){
            throw new IOException("bean tag's id is null");
        }
        String typeStr;
        if( type == null || type.getNodeName().length() == 0){
            typeStr="java.lang.String";
        }else{
            typeStr = type.getNodeValue();
        }
        
        BeanInfoMapping beanMapping =
            new BeanInfoMapping(id.getNodeValue(), typeStr);
        if( value != null && value.getNodeName().length() > 0 ){
            String valueStr = value.getNodeValue();
            beanMapping.setValue(valueStr);
        }
        if( singleton != null && singleton.getNodeName().length() > 0){
            boolean ret = new Boolean(singleton.getNodeValue()).booleanValue();
            beanMapping.setSingleton(ret);
        }
        NodeList childs = beanInfoNode.getChildNodes();
        for( int i=0; i<childs.getLength(); i++){
            Node child = childs.item(i);
            if( child.getNodeName().equals("property")){
                createProerty(child, beanMapping);
            }
        }
        
        hashMap.put( beanMapping.getId(), (Object)beanMapping );
        
    }
    
    /**
     * &lt;bean&gt;要素の子要素（&lt;property&gt;）を解析します
     * @param propertyNode &lt;forward&gt;要素
     * @param beanMapping 解析後の&lt;forward&gt;情報を追加する
     *              ActionMappingインスタンス
     * @throws Exception
     */
    protected void createProerty(Node propertyNode, BeanInfoMapping beanMapping)
    throws IOException{
        NamedNodeMap attributes = propertyNode.getAttributes();
        Node name = attributes.getNamedItem("name");
        Node value = attributes.getNamedItem("value");
        Node ref = attributes.getNamedItem("ref");
        
        Node collection = attributes.getNamedItem("collection");
        
        if( name == null || name.getNodeName().length() == 0){
            throw new IOException("property tag's name is null.");
        }
        
        BeanPropertyInfo property = new BeanPropertyInfo();
        property.setName(name.getNodeValue());
        if(value != null){
            property.setValue(value.getNodeValue());
        }else if(ref != null){
            property.setRef(ref.getNodeValue());
        }else if( collection != null ){
            //配列要素を作成
            property.setCollection(collection.getNodeValue());
            createMappings(propertyNode, property.getAttrList());
        }
        
        beanMapping.addProperty(name.getNodeValue(), property);
    }
    
}
