/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.util.converter;

import java.lang.reflect.Array;
import java.io.StringWriter;
import java.io.StringReader;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

import jp.ossc.nimbus.io.CSVReader;
import jp.ossc.nimbus.io.CSVWriter;

/**
 * CSVコンバータ。<p>
 * 
 * @author M.Takata
 */
public class CSVConverter
 implements StringConverter, ReversibleConverter, java.io.Serializable{
    
    private static final long serialVersionUID = 6829468747310701768L;

    /**
     * CSV要素集合→CSV文字列への変換を表す変換種別定数。<p>
     */
    public static final int TO_CSV = POSITIVE_CONVERT;
    
    /**
     * CSV文字列→CSV要素集合(String[])への変換を表す変換種別定数。<p>
     */
    public static final int TO_ARRAY_ELEMENT = REVERSE_CONVERT;
    
    /**
     * CSV文字列→CSV要素集合(List<String>)への変換を表す変換種別定数。<p>
     */
    public static final int TO_LIST_ELEMENT = REVERSE_CONVERT + 1;
    
    /**
     * 変換種別。<p>
     */
    protected int convertType;
    
    /**
     * CSV要素集合→CSV文字列への変換を行うCSVWriter。<p>
     */
    protected CSVWriter writer;
    
    /**
     * CSV文字列→CSV要素集合への変換を行うCSVReader。<p>
     */
    protected CSVReader reader;
    
    /**
     * パディングを行う文字列パディングコンバータを生成する。<p>
     */
    public CSVConverter(){
        this(TO_CSV);
    }
    
    /**
     * 指定された変換種別の文字列パディングコンバータを生成する。<p>
     *
     * @param type 変換種別
     * @see #TO_CSV
     * @see #TO_ARRAY_ELEMENT
     * @see #TO_LIST_ELEMENT
     */
    public CSVConverter(int type){
        setConvertType(type);
    }
    
    // ReversibleConverterのJavaDoc
    @Override
    public void setConvertType(int type){
        switch(type){
        case TO_CSV:
        case TO_ARRAY_ELEMENT:
        case TO_LIST_ELEMENT:
            convertType = type;
            break;
        default:
            throw new IllegalArgumentException(
                "Invalid convert type : " + type
            );
        }
    }
    
    /**
     * 変換種別を取得する。<p>
     *
     * @return 変換種別
     * @see #setConvertType(int)
     */
    public int getConvertType(){
        return convertType;
    }
    
    /**
     * CSV要素集合→CSV文字列への変換を行うCSVWriterを設定する。<p>
     *
     * @param writer CSVWriter
     */
    public void setCSVWriter(CSVWriter writer){
        this.writer = writer;
    }
    
    /**
     * CSV要素集合→CSV文字列への変換を行うCSVWriterを取得する。<p>
     *
     * @return CSVWriter
     */
    public CSVWriter getCSVWriter(){
        return writer;
    }
    
    /**
     * CSV文字列→CSV要素集合への変換を行うCSVReaderを設定する。<p>
     *
     * @param reader CSVReader
     */
    public void setCSVReader(CSVReader reader){
        this.reader = reader;
    }
    
    /**
     * CSV文字列→CSV要素集合への変換を行うCSVReaderを取得する。<p>
     *
     * @return CSVReader
     */
    public CSVReader getCSVReader(){
        return reader;
    }
    
    // ConverterのJavaDoc
    @SuppressWarnings("unchecked")
    @Override
    public Object convert(Object obj) throws ConvertException{
        if(obj == null){
            return null;
        }
        try{
            switch(convertType){
            case TO_CSV:
                final StringWriter sw = new StringWriter();
                final CSVWriter w = writer == null
                    ? new CSVWriter() : writer.cloneWriter();
                w.setWriter(sw);
                if(obj.getClass().isArray()){
                    Object[] array = (Object[])obj;
                    for(Object element : array){
                        w.writeElement(element);
                    }
                }else if(obj instanceof Collection){
                    Collection<Object> col = (Collection<Object>)obj;
                    for(Object element : col){
                        w.writeElement(element);
                    }
                }else{
                    w.writeElement(obj);
                }
                w.flush();
                return sw.toString();
            case TO_ARRAY_ELEMENT:
            case TO_LIST_ELEMENT:
            default:
                final StringReader sr = new StringReader(obj.toString());
                final CSVReader r = reader == null
                    ? new CSVReader() : reader.cloneReader();
                r.setReader(sr);
                switch(convertType){
                case TO_LIST_ELEMENT:
                    return r.readCSVLineList();
                case TO_ARRAY_ELEMENT:
                default:
                    return r.readCSVLine();
                }
            }
        }catch(IOException e){
            // 起こらないはず
            throw new ConvertException(e);
        }
    }
    
    /**
     * 文字列を変換する。<p>
     * 変換文字列配列と変換キャラクタ配列を使って変換する。<br>
     *
     * @param str 変換対象の文字列 
     * @return 変換後の文字列
     * @exception ConvertException 変換に失敗した場合
     */
    @SuppressWarnings("unchecked")
    @Override
    public String convert(String str) throws ConvertException{
        switch(convertType){
        case TO_CSV:
            return (String)convert((Object)str);
        case TO_LIST_ELEMENT:
            Object ret = convert((Object)str);
            if(ret == null){
                return null;
            }
            if(ret instanceof List){
                List<String> list = (List<String>)ret;
                return list.size() == 0 ? null : list.get(0);
            }else{
                return ret.toString();
            }
        case TO_ARRAY_ELEMENT:
        default:
            ret = convert((Object)str);
            if(ret == null){
                return null;
            }
            if(ret.getClass().isArray()){
                ret = Array.getLength(ret) == 0 ? null : Array.get(ret, 0);
                return ret == null ? null : ret.toString();
            }else{
                return ret.toString();
            }
        }
    }
}
