/***
 *** Title: Set of XMLSchema 
 *** Author: Itsuki Noda
 *** Date: 2006.09.05
 ***/

package daruma.storage_manager.schema;

import daruma.xml.util.DOMDocumentFactory;
import daruma.xml.util.XMLFormatConverter;
import daruma.xml.NameSpace;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.Attr;
import org.apache.xpath.XPathAPI;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import java.util.ArrayList;
import java.util.List;
import java.io.StringReader;

import daruma.util.Itk;

//======================================================================
/**
 * XML Schema の集合を扱うクラス。
 * StorageManager::getSchema() などで使用
 */

public class XMLSchemaSet {

    //==================================================
    /**
     * Schema の１つのエントリ
     */
    public static class Entry {

	//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
	private String id ; 		/// スキーマ id
	private String targetNameSpace ; /// スキーマのターゲットNS
	private String string ; 	/// スキーマ定義の文字列
	private Element element ;	/// スキーマ定義のXML要素
	private List<String> importList ; /// import の要素。

	//------------------------------
	/**
	 * コンストラクタ
	 */
	public Entry(String string) {
	    this.id = null ;
	    this.string = null ;
	    this.element = null ;
	    this.importList = new ArrayList<String>() ;

	    set(string, true) ;
	}

	//------------------------------
	/**
	 * スキーマ文字列を設定
	 * scanp が true の場合、string を scan し、
	 */
	public Entry set(String string, boolean scanp) {

	    this.setString(string) ;

	    if(scanp) {
		scanString() ;
	    }
	    return this ;
	}

	public Entry set(String string) {
	    return set(string, true) ;
	}

	//------------------------------
	/**
	 * string を scan
	 */
	public Entry scanString() {
	    try{
		// Schema の Element を作成
		Document doc = DOMDocumentFactory.create() ;
		XMLFormatConverter.readNode(new StringReader(this.string), 
					    doc) ;
		this.element = doc.getDocumentElement() ;

		// IDを取得
		setIDTargetNSByElement() ;

		// import をチェック
		checkImport() ;

		return this ;
	    } 
	    catch(ParserConfigurationException ex) {
		ex.printStackTrace() ;
		/* !!! [06/09/06 03:21 I.Noda] !!! 
		 * !!! FIXME: should throw something
		 */
		return null ;
	    } 
	    catch(TransformerException ex) {
		ex.printStackTrace() ;
		/* !!! [06/09/06 03:21 I.Noda] !!! 
		 * !!! FIXME: should throw something
		 */
		return null ;
	    }
	}

	//------------------------------
	/**
	 * Schema Element から IDおよびTargetNS を取得
	 * 返す値は ID
	 */
	public String setIDTargetNSByElement() {
	    try {
		Attr idAttr = 
		    (Attr)XPathAPI.selectSingleNode(this.element, 
						    "@misp:id",
						    NameSpace.DefaultContext) ;
		if(idAttr != null) {
		    this.setID(idAttr.getValue()) ;
		}

		Attr nsAttr =
		    (Attr)XPathAPI.selectSingleNode(this.element, 
						    "@targetNamespace",
						    NameSpace.DefaultContext) ;
		if(nsAttr != null) {
		    this.setTargetNameSpace(nsAttr.getValue()) ;
		}
		
		return getID() ;
	    } 
	    catch(TransformerException ex) {
		ex.printStackTrace() ;
		/* !!! [06/09/06 03:21 I.Noda] !!! 
		 * !!! FIXME: should throw something
		 */
		return null ;
	    }

	}
		
	//------------------------------
	/**
	 * import を解析
	 */
	public void checkImport() throws TransformerException {
	    NodeIterator nodes = 
		XPathAPI.selectNodeIterator(this.element,
					    "//xsd:import/@namespace",
					    NameSpace.DefaultContext) ;

	    Node node = null ;
	    while((node = nodes.nextNode()) != null) {
		String nsURI = ((Attr)node).getValue() ;
		this.importList.add(nsURI) ;
	    }
					
	}

	//------------------------------
	/**
	 * importList を取得
	 */
	public List<String> getImportList() {
	    return this.importList ;
	}

	//------------------------------
	/**
	 * id を取得
	 */
	public String getID() {
	    return this.id ;
	}

	//------------------------------
	/**
	 * id を設定
	 */
	public String setID(String id) {
	    return this.id = id ;
	}

	//------------------------------
	/**
	 * id を取得
	 */
	public String getTargetNameSpace() {
	    return this.targetNameSpace ;
	}

	//------------------------------
	/**
	 * id を設定
	 */
	public String setTargetNameSpace(String nsURI) {
	    return this.targetNameSpace = nsURI ;
	}

	//------------------------------
	/**
	 * schema 文字列を取得
	 */
	public String getString() {
	    try {
		if(this.string == null && this.element != null) {
		    this.string = 
			XMLFormatConverter.toString(this.element, false) ;
		}
		return this.string ;
	    } catch(TransformerException ex) {
		ex.printStackTrace() ;
		/* !!! [06/09/06 03:21 I.Noda] !!! 
		 * !!! FIXME: should throw something
		 */
		return null ;
	    }
	}

	//------------------------------
	/**
	 * schema 文字列をセット
	 */
	public String setString(String string) {
	    return this.string = string ;
	}
    }

    //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    private List<Entry> list ;
    
    //--------------------------------------------------
    /**
     * constructor
     */
    public XMLSchemaSet() {
	this.list = new ArrayList<Entry>() ;
    }
    
    //--------------------------------------------------
    /**
     * add
     * return: 登録した entry 。もし既存であれば、null を返す。
     */
    public Entry add(String schemaStr) {
	Entry entry = new Entry(schemaStr) ;
	Entry oldEntry = getEntryByID(entry.getID()) ;
	if(oldEntry == null) {
	    this.list.add(entry) ;
	    return entry ;
	} else {
	    return null ;
	}
    }

    //--------------------------------------------------
    /**
     * ID により Entry を取得。なければ null 。
     */
    public Entry getEntryByID(String uri) {
	for(Entry entry : this.list) {
	    if(entry.getID().equals(uri)) {
		return entry ;
	    }
	}
	return null ;
    }

    //--------------------------------------------------
    /**
     * list を取得
     */
    public List<Entry> getList() {
	return this.list ;
    } 

    //--------------------------------------------------
    /**
     * entry 数 を取得
     */
    public long size() {
	return this.list.size() ;
    } 
    
}
