    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.plugin.io;
    017
    018import java.io.PrintWriter;
    019import java.util.List;
    020import java.util.Locale;
    021import java.util.Map ;
    022import java.util.LinkedHashMap ;
    023
    024import org.opengion.fukurou.util.HybsEntry;
    025import org.opengion.hayabusa.db.DBTableModel;
    026
    027/**
    028 * TableWriter をJSON形式で出力する為の実装クラスです。
    029 * DefaultTableWriter を継承していますので，ラベル，名前，データの出力部のみ
    030 * オーバーライドして，JSON形式ファイルの出力機能を実現しています。
    031 *
    032 * 出力のJSON形式(JavaScript Object Notation)は、JavaScriptにおける
    033 * オブジェクトの表記法をベースとした軽量なデータ記述言語です。
    034 * このクラスでは、基本的にはすべてを文字列として処理しますので、
    035 * 数字や、true,false も、ダブルコーテーションでくくることになります。
    036 * ただし、null の場合は、null 表記になりますが、通常 空文字 "" になる場合もあります。
    037 * 1レコードのみの場合でも、配列要素として取り扱います。
    038 * UTF-8 でエンコードし、MIMEタイプはapplication/json、拡張子はjson とするのが一般的です。
    039 *
    040 * [
    041 *   { "カラム1":"値1"  ,  "カラム2":"値2"  , … } ,                     １レコード目
    042 *   { "カラム1":"値1"  ,  "カラム2":"値2"  , … } ,                     ２レコード目
    043 *    ・・・・
    044 *   { "カラム1":"値1"  ,  "カラム2":"値2"  , … }                               Ｎレコード目
    045 * ]
    046 *
    047 * writeTableParamタグで、key="JsonName" value="パラメータ名" を指定すると、
    048 * JSON形式で、配列をオブジェクトとしてまとめるパラメータを指定する事が可能です。
    049 *
    050 * {
    051 * パラメータ名:[
    052 *   { "カラム1":"値1"  ,  "カラム2":"値2"  , … } ,                     １レコード目
    053 *   { "カラム1":"値1"  ,  "カラム2":"値2"  , … } ,                     ２レコード目
    054 *    ・・・・
    055 *   { "カラム1":"値1"  ,  "カラム2":"値2"  , … }                               Ｎレコード目
    056 * ]
    057 * }
    058 *
    059 * writeTableParamタグで、key="LowerCase" value="true" を指定すると、
    060 * すべてのカラムを小文字で出力します。（データ受信側の都合を配慮）
    061 * 初期値は、false なので、基本的に大文字になります。
    062 *
    063 * @og.rev 5.6.0.3 (2012/01/24) 新規作成
    064 * @og.group ファイル出力
    065 *
    066 * @version  4.0
    067 * @author   Kazuhiko Hasegawa
    068 * @since    JDK5.0,
    069 */
    070public class TableWriter_JSON extends TableWriter_Default {
    071        //* このプログラムのVERSION文字列を設定します。   {@value} */
    072        private static final String VERSION = "6.0.1.2 (2014/08/08)" ;
    073
    074        // 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。
    075        private static final Map<String,String> keysMap = new LinkedHashMap<String,String>();
    076
    077        private String  jsonName    = null;             // JSON形式で、配列をオブジェクトとしてまとめるパラメータを指定する場合に使う。
    078        private boolean toLowerCase = false;    // カラム名(=パラメータ名)を小文字にする場合、true にセットします。初期値は大文字のまま。
    079
    080        static {
    081                keysMap.put( "JSONNAME"         , "JSON形式で、配列をオブジェクトとしてまとめる場合に使う"                                       );
    082                keysMap.put( "LOWERCASE"        , "カラム名(=パラメータ名)を小文字にする場合、true をセット(初期値:false)" );
    083        }
    084
    085        /**
    086         * DBTableModel から データを作成して,PrintWriter に書き出します。
    087         *
    088         * @param       writer PrintWriterオブジェクト
    089         */
    090        @Override
    091        public void writeDBTable( final PrintWriter writer )  {
    092                super.setHeaderSequence( "D" );
    093                super.writeDBTable( writer );
    094        }
    095
    096        /**
    097         * PrintWriter に DBTableModelのテーブル情報を書き込みます。
    098         *
    099         * 出力のXML形式は、ORACLE XDK での出力ファイルと同じ形式ですので、直接データベースに
    100         * 登録することができます。
    101         *
    102         * @og.rev 5.6.0.3 (2012/01/24) JSON形式のフォーマットを作成します。
    103         * @og.rev 5.6.1.2 (2013/02/22) jsonName もダブルクオートで囲う。
    104         * @og.rev 6.0.1.2 (2014/08/08) カラム飛ばしできる機能を追加
    105         *
    106         * @param       table  DBTableModelオブジェクト
    107         * @param       writer PrintWriterオブジェクト
    108         */
    109        @Override
    110        protected void writeData( final DBTableModel table,final PrintWriter writer ) {
    111                int numberOfRows =  table.getRowCount();
    112
    113                String[] names = table.getNames();
    114                if( toLowerCase ) {
    115                        for( int i=0; i<names.length; i++ ) {
    116                                names[i] = names[i].toLowerCase(Locale.JAPAN);
    117                        }
    118                }
    119
    120                if( jsonName != null ) {
    121                        writer.println( "{" );
    122                        writer.print( "\"" );                   // 5.6.1.2 (2013/02/22) jsonName もダブルクオートで囲う。
    123                        writer.print( jsonName );
    124                        writer.print( "\":" );                  // 5.6.1.2 (2013/02/22) jsonName もダブルクオートで囲う。
    125                }
    126
    127                writer.println( "[" );
    128
    129                for( int row=0; row<numberOfRows; row++ ) {
    130                        if( row > 0 ) { writer.print( "," ); }                  // 最初以後の連結は、, を使う
    131                        writer.print( "{" );
    132                        for( int i=0; i<numberOfColumns; i++ ) {
    133                                if( i > 0 ) { writer.print( "," ); }            // 最初以後の連結は、, を使う
    134                                int clm = clmNo[i];
    135                                if( clm < 0 ) { continue; }                     // 6.0.1.2 (2014/08/08) カラム飛ばし
    136
    137                                String val = table.getValue(row,clm);
    138                                // \(バックスラッシュ) と "(ダブルクオート)のエスケープ
    139                                if( val.contains( "\\" ) || val.contains( "\"" ) ) {
    140                                        StringBuilder sb = new StringBuilder();
    141                                        for( int j=0; j<val.length(); j++ ) {
    142                                                char ch = val.charAt( j );
    143                                                if( ch == '\\' || ch == '\"' ) { sb.append( '\\' ); }
    144                                                sb.append( ch );
    145                                        }
    146                                        val = sb.toString();
    147                                }
    148
    149                                writer.print( '"' );
    150                                writer.print( names[clm] );
    151                                writer.print( "\":\"" );
    152                                writer.print( val );
    153                                writer.print( '"' );
    154                        }
    155                        writer.println( "}" );
    156                }
    157
    158                writer.println( "]" );
    159
    160                if( jsonName != null ) {
    161                        writer.println( "}" );
    162                }
    163        }
    164
    165        /**
    166         * パラメーターリストをセットします。
    167         * JSONのパラメータ名を指定します。
    168         * 引数が、null の場合は、何もしません。
    169         *
    170         * @og.rev 5.6.0.3 (2012/01/24) JSONのパラメータ名を指定します。
    171         * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。
    172         *
    173         * @param       listParam       HybsEntryリスト
    174         */
    175        @Override
    176        public void setParam( final List<HybsEntry> listParam ) {
    177                if( listParam != null && ! listParam.isEmpty() ) {
    178                        for( HybsEntry entry : listParam ) {
    179                                String key = entry.getKey() ;           // 5.6.6.1 (2013/07/12) keys の整合性チェック
    180                                checkParam( key , keysMap );
    181
    182                                String val = entry.getValue() ;         // 5.6.6.1 (2013/07/12) val を先に取得
    183                                if( val != null && val.length() > 0 ) {
    184                                        // JSON形式で、配列をオブジェクトとしてまとめるパラメータを指定する場合に使う。
    185                                        if( "JsonName".equalsIgnoreCase( key ) ) {
    186                                                jsonName = val ;
    187                                        }
    188                                        // カラム名(=パラメータ名)を小文字にする場合、true にセットします。初期値はfalse:大文字のまま。
    189                                        else if( "LowerCase".equalsIgnoreCase( key ) ) {
    190                                                toLowerCase = Boolean.parseBoolean( val );
    191                                        }
    192                                }
    193                        }
    194                }
    195        }
    196}