/*
 * $Id: ConfigUtil.java,v 1.2 2005/05/22 14:58:43 sugimotokenichi Exp $
 * Copyright (C) 2005 SUGIMOTO Ken-ichi
 * 作成日: 2005/04/10
 */
package feat.v1.config;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.util.List;

import feat.v1.InitializationException;

/**
 * 設定ファイルに関連したユーティリティ
 * @author SUGIMOTO Ken-ichi
 */
public class ConfigUtil {

    public static void init(List c) throws InitializationException {
        for(int i=0; i<c.size(); i++) {
            ConfigElement e = (ConfigElement)c.get(i);
            if ( e != null )
                e.init();
        }
    }

    public static void validate(List c) throws InitializationException {
        for(int i=0; i<c.size(); i++) {
            ConfigElement e = (ConfigElement)c.get(i);
            if ( e != null )
                e.validate();
        }
    }

    /**
     * リストの中から名前がnameのオブジェクトを探す。
     * @param c
     * @param name
     * @return 見つかったオブジェクト。見つからなかったらnull。
     */
    public static ConfigElement find(List c, String name) {
        if ( name == null || c == null )
            return null;

        for(int i=0; i<c.size(); i++) {
            ConfigElement e = (ConfigElement)c.get(i);
            if ( e != null && e.getName().equals(name) )
                return e;
        }

        return null;
    }

    public static String getXMLEncoding(FileLocator file) {
        //System.out.println(file.toString());
        BufferedInputStream in = null;
        try {
	        in = new BufferedInputStream(file.openInputStream());
	        int mode = 0;
	        String attributeName = null;
	        while(true) {
	            switch( mode ) {

	                // "<?xml"を見つける
	                case 0: {
	                    skip('<', true, in);
	                    if ( read("?xml", in) )
	                        mode = 1;
	                    break;
	                }

                    // 属性を読み取る
                	case 1:
	                    attributeName = null;
                	    if ( !readWhitespace(in) )
                	        mode = 0;
                	    if ( read("encoding", in) ) {
                	        attributeName = "encoding";
                	        mode = 2;
                	    }
                	    else if ( read("version", in) ) {
                	        attributeName = "version";
                	        mode = 2;
                	    }
                	    else if ( read("standalone", in) ) {
                	        attributeName = "standalone";
                	        mode = 2;
                	    }
                	    else
                	        return null;
                	    break;

            	    // 属性値を読み取る
                	case 2: {
                	    readWhitespace(in);
                	    if ( read("=", in) ) {
                	        readWhitespace(in);
                	        // ""の中を読み取る
                	        String value = null;
                	        if ( read("\"", in) ) {
                	            value = skip('"', false, in);
                	        }
                	        else if ( read("'", in) ) {
                	            value = skip('\'', false, in);
                	        }
                	        //System.out.println(attributeName+"="+value);
            	            if ( attributeName.equals("encoding") && value != null ) {
            	                return value;
            	            }
                	    }
                	    mode = 1;
                	    break;
                	}
	            }
	        }
        }
        catch (IOException ex) {
            return null;
        }
        finally {
            if ( in != null )
                try {
                    in.close();
                }
            catch (IOException ex) {}
        }
    }

    /**
     * 指定の文字が現れるまでストリームから文字を読む。
     * @param tail
     * @param discard 読み取った文字列を返さずに捨てるならtrue
     * @param in
     * @return tailが現れるまでに読み取った文字。tailは含まない
     * @throws IOException
     * @throws EOFException tailが現れる前にストリームの最後に到達した
     */
    private static String skip(char tail, boolean discard, BufferedInputStream in) throws IOException, EOFException {
        StringBuffer buf = discard ? null : new StringBuffer();
        in.mark(256);
        int c;
        do {
            c = in.read();
            if ( c == -1 ) {
                in.reset();
                throw new EOFException();
            }
            else if ( c != tail && buf != null ) {
                buf.append((char)c);
            }
        }
        while ( c != tail );

        if ( buf != null ) {
            return buf.toString();
        }
        else
        	return null;
    }

    /**
     * ストリームから文字列を読み取る。
     * ストリームの現在の位置に指定の文字列から無かったら、
     * 読み取り位置をこのメソッドの開始時点に戻す。
     * @param string
     * @param in
     * @return
     * @throws IOException
     */
    private static boolean read(String string, BufferedInputStream in) throws IOException {
        char[] cstr = string.toLowerCase().toCharArray();
        in.mark(cstr.length);
        for(int idx=0; idx < cstr.length; idx++) {
            int c = in.read();

            // 最後まで読んでしまったらfalse
            if ( c == -1 ) {
                in.reset();
                return false;
            }

            // １文字読んで比較する
            char ch = Character.toLowerCase((char)c);
            if ( ch != cstr[idx] ) {
                in.reset();
                return false;
            }
        }

        return true;
    }

    /**
     * 空白文字を読み取る。ストリームの現在の位置は空白文字の次の文字にセットされる。
     * @param in
     * @return 空白文字があったらtrue。無ければfalse。
     * @throws IOException
     */
    private static boolean readWhitespace(BufferedInputStream in) throws IOException {
        boolean ret = false;
        while( true ) {
    	    in.mark(16);
            char c = (char)in.read();
            if ( Character.isWhitespace(c) ) {
                ret = true;
            }
            else {
                in.reset();
                return ret;
            }
        }
    }


}
