/*
 * 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.beans.dataset;

import java.util.*;

/**
 * 汎用的なデータ集合を表すBean。<p>
 * 複数の{@link Header ヘッダー}と、複数の{@link RecordList レコードリスト}を名前と紐付けて管理する。<br>
 * ヘッダーは複数のプロパティを持つBeanで、レコードリストは、ヘッダーと同様に複数のプロパティを持つ{@link Record レコード}というBeanをリスト化したBeanである。<br>
 * ヘッダー、及びレコードリストのレコードは、どのようなBeanにするのか（プロパティ名、型など）をレコードスキーマで定義して、動的なBeanを作る事ができる。<br>
 * {@link RecordSchema レコードスキーマ}は、{@link PropertySchema プロパティスキーマ}の集合であり、<br>
 * <pre>
 *   プロパティスキーマの実装クラス名:プロパティスキーマ定義
 *   プロパティスキーマの実装クラス名:プロパティスキーマ定義
 *                   :
 * </pre>
 * というように、プロパティの数だけ改行区切りで定義する。<br>
 * また、プロパティスキーマの実装クラス名は省略可能で、省略した場合は、{@link DefaultPropertySchema}が適用される。<br>
 * 以下にサンプルコードを示す。<br>
 * <pre>
 *     import jp.ossc.nimbus.beans.dataset.*;
 *     
 *     // データセットを生成
 *     DataSet dataSet = new DataSet("sample");
 *     
 *     // データセットのスキーマを以下のように定義する
 *     // ヘッダ：
 *     //   プロパティ名  型
 *     //        A        java.lang.String
 *     //        B        long
 *     // 
 *     // レコードリスト：
 *     //   プロパティ名  型
 *     //        C        int
 *     //        D        java.lang.String
 *     //        E        java.lang.String
 *     dataSet.setSchema(
 *         ":A,java.lang.String\n"
 *             + ":B,long",
 *         ":C,int\n"
 *             + ":D,java.lang.String\n"
 *             + ":E,java.lang.String"
 *     );
 *     
 *     // ヘッダを取得して値を設定する
 *     Header header = dataSet.getHeader();
 *     header.setProperty("A", "hoge");
 *     header.setProperty("B", 100l);
 *     
 *     // レコードリストを取得する
 *     RecordList recordList = dataSet.getRecordList();
 *     // レコード1を生成して、値を設定する
 *     Record record1 = recordList.createRecord();
 *     record1.setProperty("C", 1);
 *     record1.setProperty("D", "hoge1");
 *     record1.setProperty("E", "fuga1");
 *     recordList.addRecord(record1);
 *     // レコード2を生成して、値を設定する
 *     Record record2 = recordList.createRecord();
 *     record2.setProperty("C", 2);
 *     record2.setProperty("D", "hoge2");
 *     record2.setProperty("E", "fuga2");
 *     recordList.addRecord(record2);
 * </pre>
 *
 * @author M.Takata
 */
public class DataSet implements java.io.Serializable, Cloneable{
    
    private static final long serialVersionUID = 452460154073106633L;
    
    /**
     * データセットの名前。<p>
     */
    protected String name;
    
    /**
     * ヘッダーのマップ。<p>
     * キーはヘッダー名、値は{@link Header ヘッダー}
     */
    protected Map<String, Header> headerMap;
    
    /**
     * レコードリストのマップ。<p>
     * キーはレコードリスト名、値は{@link RecordList レコードリスト}
     */
    protected Map<String, RecordList> recordListMap;
    
    /**
     * ネストされたレコードリストのスキーマのマップ。<p>
     * キーはレコードリスト名、値はレコードスキーマ文字列
     */
    protected transient Map<String, String> nestedRecordListMap;
    
    /**
     * ネストされたレコードのスキーマのマップ。<p>
     * キーはレコード名、値は{@link RecordSchema レコードスキーマ}
     */
    protected transient Map<String, String> nestedRecordMap;
    
    /**
     * 空のデータセットを生成する。<p>
     */
    public DataSet(){
    }
    
    /**
     * 名前付きのデータセットを生成する。<p>
     *
     * @param name 名前
     */
    public DataSet(String name){
        this.name = name;
    }
    
    /**
     * データセット名を取得する。<p>
     *
     * @return データセット名
     */
    public String getName(){
        return name;
    }
    
    /**
     * データセット名を設定する。<p>
     *
     * @param name データセット名
     */
    public void setName(String name){
        this.name = name;
    }
    
    /**
     * 名前を持たない{@link Header ヘッダー}のスキーマを設定する。<p>
     * {@link #setHeaderSchema(String, String) setHeaderSchema(null, schema)}を呼び出すのと同じ。<br>
     *
     * @param schema スキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setHeaderSchema(String schema)
     throws PropertySchemaDefineException{
        setHeaderSchema(null, schema);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}を生成する。<p>
     *
     * @param name ヘッダー名
     * @param schema スキーマ
     * @return ヘッダー
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    protected Header createHeader(String name, String schema)
     throws PropertySchemaDefineException{
        return new Header(name, schema);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}を生成する。<p>
     *
     * @param name ヘッダー名
     * @param schema スキーマ
     * @return ヘッダー
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    protected Header createHeader(String name, RecordSchema schema)
     throws PropertySchemaDefineException{
        return new Header(name, schema);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}のスキーマを設定する。<p>
     *
     * @param name ヘッダー名
     * @param schema スキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setHeaderSchema(String name, String schema)
     throws PropertySchemaDefineException{
        if(headerMap == null){
            headerMap = Collections.<String, Header>synchronizedMap(new LinkedHashMap<String, Header>());
        }
        headerMap.put(name, createHeader(name, schema));
    }
    
    /**
     * 名前を持たない{@link Header ヘッダー}のスキーマを設定する。<p>
     * {@link #setHeaderSchema(String, RecordSchema) setHeaderSchema(null, schema)}を呼び出すのと同じ。<br>
     *
     * @param schema スキーマ
     */
    public void setHeaderSchema(RecordSchema schema){
        setHeaderSchema(null, schema);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}のスキーマを設定する。<p>
     *
     * @param name ヘッダー名
     * @param schema スキーマ
     */
    public void setHeaderSchema(String name, RecordSchema schema){
        if(headerMap == null){
            headerMap = Collections.<String, Header>synchronizedMap(new LinkedHashMap<String, Header>());
        }
        headerMap.put(name, createHeader(name, schema));
    }
    
    /**
     * 名前を持たない{@link RecordList レコードリスト}のスキーマを設定する。<p>
     * {@link #setRecordListSchema(String, String) setRecordListSchema(null, schema)}を呼び出すのと同じ。<br>
     *
     * @param schema スキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setRecordListSchema(String schema)
     throws PropertySchemaDefineException{
        setRecordListSchema(null, schema);
    }
    
    /**
     * 指定した名前の{@link RecordList レコードリスト}を生成する。<p>
     *
     * @param name レコードリスト名
     * @param schema スキーマ
     * @return レコードリスト
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    protected RecordList createRecordList(String name, String schema)
     throws PropertySchemaDefineException{
        return new RecordList(name, schema);
    }
    
    /**
     * 指定した名前の{@link RecordList レコードリスト}を生成する。<p>
     *
     * @param name レコードリスト名
     * @param schema スキーマ
     * @return レコードリスト
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    protected RecordList createRecordList(String name, RecordSchema schema)
     throws PropertySchemaDefineException{
        return new RecordList(name, schema);
    }
    
    /**
     * 指定した名前の{@link RecordList レコードリスト}のスキーマを設定する。<p>
     *
     * @param name レコードリスト名
     * @param schema スキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setRecordListSchema(String name, String schema)
     throws PropertySchemaDefineException{
        
        if(recordListMap == null){
            recordListMap = Collections.<String, RecordList>synchronizedMap(new LinkedHashMap<String, RecordList>());
        }
        recordListMap.put(
            name,
            createRecordList(name, schema)
        );
    }
    
    /**
     * 名前を持たない{@link RecordList レコードリスト}のスキーマを設定する。<p>
     * {@link #setRecordListSchema(String, RecordSchema) setRecordListSchema(null, schema)}を呼び出すのと同じ。<br>
     *
     * @param schema スキーマ
     */
    public void setRecordListSchema(RecordSchema schema){
        setRecordListSchema(null, schema);
    }
    
    /**
     * 指定した名前の{@link RecordList レコードリスト}のスキーマを設定する。<p>
     *
     * @param name レコードリスト名
     * @param schema スキーマ
     */
    public void setRecordListSchema(String name, RecordSchema schema){
        
        if(recordListMap == null){
            recordListMap = Collections.<String, RecordList>synchronizedMap(new LinkedHashMap<String, RecordList>());
        }
        recordListMap.put(
            name,
            createRecordList(name, schema)
        );
    }
    
    /**
     * 指定した名前のネストした{@link RecordList レコードリスト}のスキーマを設定する。<p>
     *
     * @param name レコードリスト名
     * @param schema スキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setNestedRecordListSchema(String name, String schema)
     throws PropertySchemaDefineException{
        
        if(nestedRecordListMap == null){
            nestedRecordListMap = Collections.<String, String>synchronizedMap(new LinkedHashMap<String, String>());
        }
        RecordSchema recSchema = RecordSchema.getInstance(schema);
        nestedRecordListMap.put(
            name,
            recSchema.getSchema()
        );
    }
    
    /**
     * 指定した名前のネストした{@link RecordList レコードリスト}のスキーマを設定する。<p>
     *
     * @param name レコードリスト名
     * @param schema スキーマ
     */
    public void setNestedRecordListSchema(String name, RecordSchema schema){
        
        if(nestedRecordListMap == null){
            nestedRecordListMap = Collections.<String, String>synchronizedMap(new LinkedHashMap<String, String>());
        }
        nestedRecordListMap.put(
            name,
            schema.getSchema()
        );
    }
    
    /**
     * 指定した名前のネストした{@link RecordList レコードリスト}のスキーマを取得する。<p>
     *
     * @param name レコードリスト名
     * @return スキーマ
     */
    public RecordSchema getNestedRecordListSchema(String name)
     throws PropertySchemaDefineException{
        if(nestedRecordListMap == null){
            return null;
        }
        final String schema = nestedRecordListMap.get(name);
        return schema == null ? null : RecordSchema.getInstance(schema);
    }
    
    /**
     * 定義された順に並んだネストしたレコードリスト名配列を取得する。<p>
     *
     * @return ネストしたレコードリスト名配列
     */
    public String[] getNestedRecordListSchemaNames(){
        return nestedRecordListMap == null ? new String[0] : nestedRecordListMap.keySet().toArray(new String[nestedRecordListMap.size()]);
    }
    
    /**
     * ネストしたレコードリストの数を取得する。<p>
     *
     * @return ネストしたレコードリストの数
     */
    public int getNestedRecordListSchemaSize(){
        return nestedRecordListMap == null ? 0 : nestedRecordListMap.size();
    }
    
    /**
     * ネストしたレコードリストのマップを取得する。<p>
     *
     * @return ネストしたレコードリストのマップ。キーはレコードリスト名、値はスキーマ文字列
     */
    public Map<String, String> getNestedRecordListSchemaMap(){
        if(nestedRecordListMap == null){
            nestedRecordListMap = Collections.<String, String>synchronizedMap(new LinkedHashMap<String, String>());
        }
        return nestedRecordListMap;
    }
    
    /**
     * 指定した名前のネストした{@link Record レコード}のスキーマを設定する。<p>
     *
     * @param name レコード名
     * @param schema スキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setNestedRecordSchema(String name, String schema)
     throws PropertySchemaDefineException{
        
        if(nestedRecordMap == null){
            nestedRecordMap = Collections.synchronizedMap(new LinkedHashMap<String, String>());
        }
        RecordSchema recSchema = RecordSchema.getInstance(schema);
        nestedRecordMap.put(
            name,
            recSchema.getSchema()
        );
    }
    
    /**
     * 指定した名前のネストした{@link Record レコード}のスキーマを設定する。<p>
     *
     * @param name レコード名
     * @param schema スキーマ
     */
    public void setNestedRecordSchema(String name, RecordSchema schema){
        
        if(nestedRecordMap == null){
            nestedRecordMap = Collections.synchronizedMap(new LinkedHashMap<String, String>());
        }
        nestedRecordMap.put(
            name,
            schema.getSchema()
        );
    }
    
    /**
     * 指定した名前のネストした{@link Record レコード}のスキーマを取得する。<p>
     *
     * @param name レコード名
     * @return スキーマ
     */
    public RecordSchema getNestedRecordSchema(String name)
     throws PropertySchemaDefineException{
        if(nestedRecordMap == null){
            return null;
        }
        final String schema = nestedRecordMap.get(name);
        return schema == null ? null : RecordSchema.getInstance(schema);
    }
    
    /**
     * 定義された順に並んだネストしたレコード名配列を取得する。<p>
     *
     * @return ネストしたレコード名配列
     */
    public String[] getNestedRecordSchemaNames(){
        return nestedRecordMap == null ? new String[0] : nestedRecordMap.keySet().toArray(new String[nestedRecordMap.size()]);
    }
    
    /**
     * ネストしたレコードの数を取得する。<p>
     *
     * @return ネストしたレコードの数
     */
    public int getNestedRecordSchemaSize(){
        return nestedRecordMap == null ? 0 : nestedRecordMap.size();
    }
    
    /**
     * ネストしたレコードのマップを取得する。<p>
     *
     * @return ネストしたレコードのマップ。キーはレコード名、値はスキーマ文字列
     */
    public Map<String,String> getNestedRecordSchemaMap(){
        if(nestedRecordMap == null){
            nestedRecordMap = Collections.synchronizedMap(new LinkedHashMap<String, String>());
        }
        return nestedRecordMap;
    }
    
    /**
     * 名前を持たない{@link Header ヘッダー}と{@link RecordList レコードリスト}のスキーマを設定する。<p>
     * {@link #setHeaderSchema(String, String) setHeaderSchema(null, schema)}と{@link #setRecordListSchema(String, String) setRecordListSchema(null, schema)}を呼び出すのと同じ。<br>
     *
     * @param headerSchema ヘッダーのスキーマ
     * @param recordListSchema レコードリストのスキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setSchema(String headerSchema, String recordListSchema)
     throws PropertySchemaDefineException{
        setSchema(null, headerSchema, recordListSchema);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}と{@link RecordList レコードリスト}のスキーマを設定する。<p>
     *
     * @param name ヘッダー名及びレコードリスト名
     * @param headerSchema ヘッダーのスキーマ
     * @param recordListSchema レコードリストのスキーマ
     */
    public void setSchema(
        String name,
        RecordSchema headerSchema,
        RecordSchema recordListSchema
    ){
        if(headerSchema != null){
            setHeaderSchema(name, headerSchema);
        }
        if(recordListSchema != null){
            setRecordListSchema(name, recordListSchema);
        }
    }
    
    /**
     * 名前を持たない{@link Header ヘッダー}と{@link RecordList レコードリスト}のスキーマを設定する。<p>
     * {@link #setHeaderSchema(String, RecordSchema) setHeaderSchema(null, schema)}と{@link #setRecordListSchema(String, RecordSchema) setRecordListSchema(null, schema)}を呼び出すのと同じ。<br>
     *
     * @param headerSchema ヘッダーのスキーマ
     * @param recordListSchema レコードリストのスキーマ
     */
    public void setSchema(RecordSchema headerSchema, RecordSchema recordListSchema){
        setSchema(null, headerSchema, recordListSchema);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}と{@link RecordList レコードリスト}のスキーマを設定する。<p>
     *
     * @param name ヘッダー名及びレコードリスト名
     * @param headerSchema ヘッダーのスキーマ
     * @param recordListSchema レコードリストのスキーマ
     * @exception PropertySchemaDefineException プロパティのスキーマ定義に失敗した場合
     */
    public void setSchema(
        String name,
        String headerSchema,
        String recordListSchema
    ) throws PropertySchemaDefineException{
        if(headerSchema != null){
            setHeaderSchema(name, headerSchema);
        }
        if(recordListSchema != null){
            setRecordListSchema(name, recordListSchema);
        }
    }
    
    /**
     * 名前を持たない{@link Header ヘッダー}を取得する。<p>
     * {@link #getHeader(String) getHeader(null)}を呼び出すのと同じ。<br>
     *
     * @return ヘッダー
     */
    public Header getHeader(){
        return getHeader(null);
    }
    
    /**
     * 指定した名前の{@link Header ヘッダー}を取得する。<p>
     *
     * @param name ヘッダー名
     * @return ヘッダー
     */
    public Header getHeader(String name){
        return headerMap == null ? null : headerMap.get(name);
    }
    
    /**
     * 定義された順に並んだヘッダー名配列を取得する。<p>
     *
     * @return ヘッダー名配列
     */
    public String[] getHeaderNames(){
        return headerMap == null ? new String[0] : headerMap.keySet().toArray(new String[headerMap.size()]);
    }
    
    /**
     * ヘッダーの数を取得する。<p>
     *
     * @return ヘッダーの数
     */
    public int getHeaderSize(){
        return headerMap == null ? 0 : headerMap.size();
    }
    
    /**
     * ヘッダーのマップを取得する。<p>
     *
     * @return ヘッダーのマップ。キーはヘッダー名、値は{@link Header ヘッダー}
     */
    public Map<String, Header> getHeaderMap(){
        if(headerMap == null){
            headerMap = Collections.<String, Header>synchronizedMap(new LinkedHashMap<String, Header>());
        }
        return headerMap;
    }
    
    /**
     * ヘッダーを追加する。<p>
     *
     * @param header ヘッダー
     */
    public void addHeader(Header header){
        if(headerMap == null){
            headerMap = Collections.<String, Header>synchronizedMap(new LinkedHashMap<String, Header>());
        }
        headerMap.put(header.getName(), header);
    }
    
    /**
     * 名前を持たない{@link RecordList レコードリスト}を取得する。<p>
     * {@link #getRecordList(String) getRecordList(null)}を呼び出すのと同じ。<br>
     *
     * @return レコードリスト
     */
    public RecordList getRecordList(){
        return getRecordList(null);
    }
    
    /**
     * 指定した名前の{@link RecordList レコードリスト}を取得する。<p>
     *
     * @param name レコードリスト名
     * @return レコードリスト
     */
    public RecordList getRecordList(String name){
        return recordListMap == null ? null : recordListMap.get(name);
    }
    
    /**
     * 定義された順に並んだレコードリスト名配列を取得する。<p>
     *
     * @return レコードリスト名配列
     */
    public String[] getRecordListNames(){
        return recordListMap == null ? new String[0] : recordListMap.keySet().toArray(new String[recordListMap.size()]);
    }
    
    /**
     * レコードリストの数を取得する。<p>
     *
     * @return レコードリストの数
     */
    public int getRecordListSize(){
        return recordListMap == null ? 0 : recordListMap.size();
    }
    
    /**
     * レコードリストのマップを取得する。<p>
     *
     * @return レコードリストのマップ。キーはレコードリスト名、値は{@link RecordList レコードリスト}
     */
    public Map<String, RecordList> getRecordListMap(){
        if(recordListMap == null){
            recordListMap = Collections.<String, RecordList>synchronizedMap(new LinkedHashMap<String, RecordList>());
        }
        return recordListMap;
    }
    
    /**
     * レコードリストを追加する。<p>
     *
     * @param recList レコードリスト
     */
    public void addRecordList(RecordList recList){
        if(recordListMap == null){
            recordListMap = Collections.<String, RecordList>synchronizedMap(new LinkedHashMap<String, RecordList>());
        }
        recordListMap.put(recList.getName(), recList);
    }
    
    /**
     * 指定した名前のネストした{@link RecordList レコードリスト}を生成する。<p>
     *
     * @param name レコードリスト名
     * @return レコードリスト
     */
    public RecordList createNestedRecordList(String name){
        if(nestedRecordListMap == null
             || !nestedRecordListMap.containsKey(name)){
            return null;
        }
        return createRecordList(
            name,
            RecordSchema.getInstance(nestedRecordListMap.get(name))
        );
    }
    
    /**
     * 指定した名前のネストした{@link Record レコード}を生成する。<p>
     *
     * @param name レコード名
     * @return レコード
     */
    public Record createNestedRecord(String name){
        if(nestedRecordMap == null
             || !nestedRecordMap.containsKey(name)){
            return null;
        }
        return new Record(
            RecordSchema.getInstance(nestedRecordMap.get(name))
        );
    }
    
    /**
     * データセットをクリアする。<p>
     * ヘッダーのデータとレコードリストのレコードを削除する。<br>
     */
    public void clear(){
        if(headerMap != null && headerMap.size() != 0){
            final String[] headerNames = headerMap.keySet()
                .toArray(new String[headerMap.size()]);
            for(String headerName : headerNames){
                final Header header = getHeader(headerName);
                if(header != null){
                    header.clear();
                }
            }
        }
        if(recordListMap != null && recordListMap.size() != 0){
            final String[] recListNames = recordListMap.keySet()
                .toArray(new String[recordListMap.size()]);
            for(String recListName : recListNames){
                final RecordList recList = getRecordList(recListName);
                if(recList != null){
                    recList.clear();
                }
            }
        }
    }
    
    /**
     * 名前を持たない{@link Header ヘッダー}を検証する。<p>
     *
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validateHeader() throws PropertyGetException, PropertyValidateException{
        Header header = getHeader();
        if(header == null){
            return false;
        }
        return header.validate();
    }
    
    /**
     * 指定された{@link Header ヘッダー}を検証する。<p>
     *
     * @param name ヘッダー名
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validateHeader(String name) throws PropertyGetException, PropertyValidateException{
        Header header = getHeader(name);
        if(header == null){
            return false;
        }
        return header.validate();
    }
    
    /**
     * 全ての{@link Header ヘッダー}を検証する。<p>
     *
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validateHeaders() throws PropertyGetException, PropertyValidateException{
        if(headerMap == null || headerMap.size() == 0){
            return true;
        }
        Iterator<Header> headers = headerMap.values().iterator();
        while(headers.hasNext()){
            Header header = headers.next();
            if(!header.validate()){
                return false;
            }
        }
        return true;
    }
    
    /**
     * 名前を持たない{@link RecordList レコードリスト}を検証する。<p>
     *
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validateRecordList() throws PropertyGetException, PropertyValidateException{
        RecordList recordList = getRecordList();
        if(recordList == null){
            return false;
        }
        return recordList.validate();
    }
    
    /**
     * 指定された{@link RecordList レコードリスト}を検証する。<p>
     *
     * @param name ヘッダー名
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validateRecordList(String name) throws PropertyGetException, PropertyValidateException{
        RecordList recordList = getRecordList(name);
        if(recordList == null){
            return false;
        }
        return recordList.validate();
    }
    
    /**
     * 全ての{@link RecordList レコードリスト}を検証する。<p>
     *
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validateRecordLists() throws PropertyGetException, PropertyValidateException{
        if(recordListMap == null || recordListMap.size() == 0){
            return true;
        }
        Iterator<RecordList> recordLists = recordListMap.values().iterator();
        while(recordLists.hasNext()){
            RecordList recordList = recordLists.next();
            if(!recordList.validate()){
                return false;
            }
        }
        return true;
    }
    
    /**
     * 全ての{@link Header ヘッダー}及び{@link RecordList レコードリスト}を検証する。<p>
     *
     * @return 検証結果。trueの場合、検証成功
     * @exception PropertyGetException プロパティの取得に失敗した場合
     * @exception PropertyValidateException プロパティの検証時に例外が発生した場合
     */
    public boolean validate() throws PropertyGetException, PropertyValidateException{
        if(!validateHeaders()){
            return false;
        }
        if(!validateRecordLists()){
            return false;
        }
        return true;
    }
    
    /**
     * データセットを複製する。<p>
     *
     * @return 複製したデータセット
     */
    public Object clone(){
        return cloneDataSet();
    }
    
    /**
     * 同じスキーマを持ちデータを持たない空のデータセットを複製する。<p>
     *
     * @return 複製した空のデータセット
     */
    public DataSet cloneSchema(){
        return cloneDataSet(false);
    }
    
    /**
     * データセットを複製する。<p>
     *
     * @return 複製したデータセット
     */
    public DataSet cloneDataSet(){
        return cloneDataSet(true);
    }
    
    /**
     * データセットを複製する。<p>
     *
     * @param hasData データも複製する場合true
     * @return 複製したデータセット
     */
    protected DataSet cloneDataSet(boolean hasData){
        DataSet dataSet = null;
        try{
            dataSet = (DataSet)super.clone();
            dataSet.headerMap = null;
            dataSet.recordListMap = null;
            dataSet.nestedRecordListMap = null;
            dataSet.nestedRecordMap = null;
        }catch(CloneNotSupportedException e){
            return null;
        }
        if(headerMap != null && headerMap.size() != 0){
            final String[] headerNames = headerMap.keySet()
                .toArray(new String[headerMap.size()]);
            dataSet.headerMap
                 = Collections.<String, Header>synchronizedMap(new LinkedHashMap<String, Header>());
            for(String headerName : headerNames){
                final Header header = getHeader(headerName);
                if(header != null){
                    dataSet.headerMap.put(
                        headerName,
                        hasData ? (Header)header.cloneRecord() : (Header)header.cloneSchema()
                    );
                }
            }
        }
        if(recordListMap != null && recordListMap.size() != 0){
            final String[] recListNames = recordListMap.keySet()
                .toArray(new String[recordListMap.size()]);
            dataSet.recordListMap
                 = Collections.<String, RecordList>synchronizedMap(new LinkedHashMap<String, RecordList>());
            for(String recListName : recListNames){
                final RecordList recList = getRecordList(recListName);
                if(recList != null){
                    dataSet.recordListMap.put(
                        recListName,
                        hasData ? recList.cloneRecordList()
                             : recList.cloneSchema()
                    );
                }
            }
        }
        if(nestedRecordListMap != null && nestedRecordListMap.size() != 0){
            dataSet.nestedRecordListMap
                 = Collections.<String, String>synchronizedMap(new LinkedHashMap<String, String>());
            dataSet.nestedRecordListMap.putAll(nestedRecordListMap);
        }
        if(nestedRecordMap != null && nestedRecordMap.size() != 0){
            dataSet.nestedRecordMap
                 = Collections.synchronizedMap(new LinkedHashMap<String, String>());
            dataSet.nestedRecordMap.putAll(nestedRecordMap);
        }
        return dataSet;
    }
    
    /**
     * このデータセットの文字列表現を取得する。<p>
     *
     * @return 文字列表現
     */
    @Override
    public String toString(){
        return super.toString() + "{name=" + name + '}';
    }
}