    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.fukurou.system;
    017
    018import java.util.List;                                                                                          // 6.9.2.1 (2018/03/12)
    019import java.util.ArrayList;                                                                                     // 6.9.2.1 (2018/03/12)
    020import java.util.Set;                                                                                           // 6.9.2.1 (2018/03/12)
    021import java.util.HashSet;                                                                                       // 6.9.2.1 (2018/03/12)
    022import java.util.LinkedHashSet;                                                                         // 7.0.6.4 (2019/11/29)
    023import java.util.Iterator;                                                                                      // 7.0.6.4 (2019/11/29)
    024
    025import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
    026import java.util.concurrent.ConcurrentHashMap;
    027import java.util.Collections;                                                                           // 7.0.7.2 (2019/12/28)
    028
    029import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
    030import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.4.2.0 (2016/01/29)
    031
    032/**
    033 * ThrowUtil.java は、共通的に使用される Throwable,Exception関連メソッドを集約した、クラスです。
    034 *
    035 * StringUtil にあったメソッドを、こちらに移動させました。
    036 *
    037 * @og.group ユーティリティ
    038 *
    039 * @og.rev 6.4.2.0 (2016/01/29) 新規作成
    040 * @og.rev 6.4.3.2 (2016/02/19) 全面書き換え
    041 *
    042 * @version  6.0
    043 * @author       Kazuhiko Hasegawa
    044 * @since    JDK8.0,
    045 */
    046public final class ThrowUtil {
    047        /** スタックトレースする行数        {@value} */
    048        public static final int MIN_STACK_SIZE = 3;                                     // 先頭から、通常にスタックトレースする行数。
    049
    050        // 6.9.2.1 (2018/03/12) 複数個所で呼ばれた場合の処理が、うまく出来ないので、暫定対策。(同時起動で、混ざる)
    051        private static final CaseBuilder C_BUF = new CaseBuilder();
    052
    053        // 6.9.3.0 (2018/03/26) static で持ちます。
    054        private static final StackTraceElement NULL_STE = new StackTraceElement( "","","",-1 );
    055
    056        // 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    057        private static final Set<Integer> CACHE_TH = new LinkedHashSet<>();     // 7.0.6.4 (2019/11/29)
    058
    059        // 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    060        private static final int MAX_CACHE_TH = 10;                                                     // 7.0.6.4 (2019/11/29)
    061
    062        /**
    063         *      デフォルトコンストラクターをprivateにして、
    064         *      オブジェクトの生成をさせないようにする。
    065         *
    066         */
    067        private ThrowUtil() {}
    068
    069        /**
    070         * Throwable の printStackTrace() 結果の内、opengion に関する箇所だけを文字列に変換して返します。
    071         *
    072         * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動。
    073         * @og.rev 6.4.2.0 (2016/01/29) すべてのスタックトレースを行っていたが、絞り込みます。
    074         *
    075         * @param    th   printStackTraceすべき元のThrowableオブジェクト
    076         *
    077         * @return   Throwableの詳細メッセージ( th.printStackTrace() )
    078         * @og.rtnNotNull
    079         */
    080        public static String ogStackTrace( final Throwable th ) {
    081                return ogStackTrace( null , th );
    082        }
    083
    084        /**
    085         * Throwable の printStackTrace() 結果の内、opengion に関する箇所だけを文字列に変換して返します。
    086         *
    087         * printStackTrace() すると、膨大なメッセージが表示されるため、その中の、"org.opengion" を
    088         * 含む箇所だけを、抜粋します。
    089         * また、同じ内容のエラーも除外します。
    090         * 先頭から、MIN_STACK_SIZE 行は、元のままのメッセージを出力します。
    091         * Throwable#getCause() で、再帰的に原因をさかのぼります。
    092         *
    093         * @og.rev 5.7.2.0 (2014/01/10) 新規作成
    094         * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動し、msg 引数を追加。
    095         * @og.rev 6.4.3.2 (2016/02/19) (通常try-with-resources文によって)抑制された例外も出力します。
    096         * @og.rev 6.5.0.1 (2016/10/21) メッセージに、BuildNumber.ENGINE_INFO を含める。
    097         * @og.rev 6.9.0.1 (2018/02/05) causeの対応が中途半端だったので、修正します。
    098         * @og.rev 6.9.2.1 (2018/03/12) 最小件数のbreak判定の方法を見直します。
    099         * @og.rev 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    100         *
    101         * @param    msg 合成したいメッセージ
    102         * @param    th 元のThrowableオブジェクト
    103         *
    104         * @return   Throwableの詳細メッセージ( StackTraceElement の抜粋 )
    105         * @og.rtnNotNull
    106         */
    107        public static String ogStackTrace( final String msg,final Throwable th ) {
    108                C_BUF.init()
    109                        .append( "Version: " , BuildNumber.ENGINE_INFO )                                        // 6.5.0.1 (2016/10/21) エンジンのバージョン
    110                        .append( "Message: " , msg );                                                                           // 追加メッセージ
    111
    112//              if( th != null ) {
    113                if( isNewThr( th ) ) {                                                                                                  // 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    114                        // Throwable.toString() で、クラス名とメッセージを分けて、それぞれを重複チェックする。
    115                        C_BUF.append( "Error  : " , th.getClass().getCanonicalName() )          // クラス名
    116                                 .append( "         " , th.getLocalizedMessage() )                              // メッセージ
    117                                 .addStackTrace( th , MIN_STACK_SIZE );                                                 // 6.9.2.1 (2018/03/12)
    118
    119                        // 原因の Throwable
    120                        Throwable tmpTh = th.getCause();
    121                        while( tmpTh != null ) {
    122                                 C_BUF.append( "Cause  : " , tmpTh.getClass().getCanonicalName() )      // クラス名
    123                                          .append( "         " , tmpTh.getLocalizedMessage() )                  // メッセージ
    124                                          .addStackTrace( tmpTh , MIN_STACK_SIZE );                                             // 6.9.2.1 (2018/03/12)
    125
    126                                tmpTh = tmpTh.getCause();
    127                        }
    128
    129                        // 6.4.3.2 (2016/02/19) (通常try-with-resources文によって)抑制された例外も出力します。
    130                        // このThrowable は、原因の Throwable は、求めません。
    131                        for( final Throwable supTh : th.getSuppressed() ) {
    132                                C_BUF.append( "Suppressed : " , supTh.getClass().getCanonicalName() )   // クラス名
    133                                         .append( "             " , supTh.getLocalizedMessage() )                       // メッセージ
    134                                         .addStackTrace( supTh , MIN_STACK_SIZE );                                                      // 6.9.2.1 (2018/03/12)
    135                        }
    136                }
    137
    138                return C_BUF.toString().trim();
    139        }
    140
    141        /**
    142         * 発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列を返します。
    143         *
    144         * 通常、System.out.println() で済ましていたエラーメッセージに対して、
    145         * エラー発生場所を特定する為の情報を付与したメッセージを作成します。
    146         * 発生場所の行番号は、このメソッド（実施は、オーバーロード先）で new Throwable して、取得します。
    147         * 呼ぶ場所で、new Throwable() する代わりの簡易目祖度になります。
    148         *
    149         * @og.rev 6.4.2.0 (2016/01/29) 新規作成。
    150         *
    151         * @param    msg 合成したいメッセージ
    152         *
    153         * @return   発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列
    154         * @og.rtnNotNull
    155         */
    156        public static String ogThrowMsg( final String msg ) {
    157                return ogThrowMsg( msg , null );
    158        }
    159
    160        /**
    161         * 発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列を返します。
    162         *
    163         * 通常、System.out.println() で済ましていたエラーメッセージに対して、
    164         * エラー発生場所を特定する為の情報を付与したメッセージを作成します。
    165         * 行番号は、引数のThrowableの最初の情報のみ使用する為、通常は、このメソッドを
    166         * 呼ぶ場所で、new Throwable() します。
    167         * 発生クラスは、org.opengion か、org.apache.jsp.jsp を含む３行のみの簡易メッセージとします。
    168         *
    169         * @og.rev 6.3.6.1 (2015/08/28) 新規作成
    170         * @og.rev 6.3.6.1 (2015/08/28) メッセージに、th.getLocalizedMessage() を含める。
    171         * @og.rev 6.3.9.0 (2015/11/06) thのnullチェックを先に行う。
    172         * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動するとともに、メソッド名を、ogErrMsg → ogThrowMsg に変更。
    173         * @og.rev 6.5.0.1 (2016/10/21) メッセージに、BuildNumber.ENGINE_INFO を含める。
    174         * @og.rev 6.9.0.1 (2018/02/05) causeの対応が中途半端だったので、修正します。
    175         * @og.rev 6.9.2.1 (2018/03/12) 最小件数のbreak判定の方法を見直します。
    176         * @og.rev 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    177         *
    178         * @param    msg 合成したいメッセージ
    179         * @param    th  元のThrowableオブジェクト
    180         *
    181         * @return   発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列
    182         * @og.rtnNotNull
    183         */
    184        public static String ogThrowMsg( final String msg,final Throwable th ) {
    185                C_BUF.init()
    186                        .append( "Version: " , BuildNumber.ENGINE_INFO )                                        // 6.5.0.1 (2016/10/21) エンジンのバージョン
    187                        .append( "Message: " , msg );                                                                           // 追加メッセージ
    188
    189//              if( th != null ) {
    190                if( isNewThr( th ) ) {                                                                                                  // 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    191                        C_BUF.append( "Error  : " , th.getClass().getCanonicalName() )          // クラス名
    192                                 .append( "         " , th.getLocalizedMessage() );                             // メッセージ
    193
    194                        // 原因の Throwable
    195                        Throwable tmpTh = th.getCause();
    196                        while( tmpTh != null ) {
    197                                 C_BUF.append( "Cause  : " , tmpTh.getClass().getCanonicalName() )      // クラス名
    198                                          .append( "         " , tmpTh.getLocalizedMessage() );                 // メッセージ
    199
    200                                tmpTh = tmpTh.getCause();
    201                        }
    202
    203                        // 6.4.3.2 (2016/02/19) (通常try-with-resources文によって)抑制された例外も出力します。
    204                        // このThrowable は、原因の Throwable は、求めません。
    205                        for( final Throwable supTh : th.getSuppressed() ) {
    206                                C_BUF.append( "Suppressed : " , supTh.getClass().getCanonicalName() )   // クラス名
    207                                         .append( "             " , supTh.getLocalizedMessage() );                      // メッセージ
    208                        }
    209                }
    210
    211                return C_BUF.toThrowMsg().trim();
    212        }
    213
    214        /**
    215         * Throwable の getStackTrace() 結果の内、opengion に関する箇所だけのStackTraceElement配列を選別して返します。
    216         *
    217         * 通常、スタックトレース情報は、膨大なメッセージが含まれるため、その中の、org.opengion か、org.apache.jsp.jsp を
    218         * 含む箇所だけを、抜粋します。
    219         * ただし、ThrowUtilクラス(このクラス)内で、スタックトレースを new しているため、このクラスで発生した
    220         * スタックトレースは、含まないようにしています。このクラスからエラーが発生しないことを望みます。
    221         * 処理は、先頭から、minCnt件数までは、そのまま残し、それ以降は、関連するトレース情報のみ残します。
    222         *
    223         * @og.rev 6.9.2.1 (2018/03/12) 選別だけを別に用意します。
    224         * @og.rev 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    225         *
    226         * @param    th         元のThrowableオブジェクト(!= null 保障済み)
    227         * @param    minCnt     StackTraceElementを登録する最小件数
    228         * @return   選別されたStackTraceElement配列
    229         * @og.rtnNotNull
    230         * @see         java.lang.Throwable#getStackTrace()
    231         */
    232        public static StackTraceElement[] selectElement( final Throwable th , final int minCnt ) {
    233                final List<StackTraceElement> list = new ArrayList<>();
    234
    235                if( th != null ) {
    236                        int idx = 0;
    237                        final Set<String> cacheSet = new HashSet<>();                   // 同一スタックの除外用
    238                        for( final StackTraceElement stEle : th.getStackTrace() ) {
    239                                final String stkMsg = stEle.toString();
    240                                if( !cacheSet.add( stkMsg ) ) { continue; }                     // 同じメッセージを出さない対応
    241
    242                                final String cls = stEle.getClassName();
    243
    244                                boolean flag = true;                            // 連続で、出力しない。
    245                                if( minCnt < 0 || idx < minCnt || cls.contains( "org.opengion" ) || cls.contains( "org.apache.jsp.jsp" ) ) {
    246                                        list.add( stEle );
    247                                        flag = true;                                    // 省略解除
    248                                }
    249                                else {
    250                                        if( flag ) {
    251                                                list.add( NULL_STE );           // StackTraceElementの省略
    252                                                flag = false;                           // 連続で、出力しない。
    253                                        }
    254                                }
    255                                idx++ ;
    256                        }
    257                }
    258
    259                return list.toArray( new StackTraceElement[list.size()] );
    260        }
    261
    262        /**
    263         * Throwable の重複チェックを行います。
    264         *
    265         * 新規の Throwable なら、true を、null か、重複している場合は、false を返します。
    266         * MAX_CACHE_TH で、最大キャッシュ数を指定します。それ以上になった場合は、
    267         * 最初に登録した Throwable を削除します。
    268         *
    269         * @og.rev 7.0.6.4 (2019/11/29) Throwable ｵﾌﾞｼﾞｪｸﾄの重複を取り除きます。
    270         * @og.rev 7.0.7.2 (2020/01/09) iterator()は、next() してからしかremove()できない。
    271         *
    272         * @param    th   printStackTraceすべき元のThrowableオブジェクト
    273         *
    274         * @return   新規なら true , 重複か null なら、false
    275         * @og.rtnNotNull
    276         */
    277        private static boolean isNewThr( final Throwable th ) {
    278                synchronized( CACHE_TH ) {
    279                        if( th != null && CACHE_TH.add( Integer.valueOf( th.hashCode() ) ) ) {
    280                                // 制限数を超えた場合に、最初のｵﾌﾞｼﾞｪｸﾄを削除します。
    281                                if( CACHE_TH.size() > MAX_CACHE_TH ) {
    282                                        final Iterator<Integer> ite = CACHE_TH.iterator();
    283                                        ite.next();                                                                                             // 7.0.7.2 (2020/01/09)
    284                                        ite.remove();
    285                                }
    286                                return true;
    287                        }
    288                }
    289                return false;
    290        }
    291
    292        /**
    293         * StringBuilder を、例外処理のスタックに特化した形で作り直した内部クラスです。
    294         *
    295         * printStackTrace() すると、膨大なメッセージが表示されるため、その中の、"org.opengion" と
    296         * "org.apache.jsp.jsp" を含む箇所だけを、抜粋します。
    297         * また、同じ内容のエラーも除外します。
    298         *
    299         * ※ 怪しい実装
    300         *    StackTrace は、内部的に、色々な箇所で呼ばれたり、同じ要因で、何度も再作成されたりします。
    301         *    この内部クラスも、ひとつの例外で、何度も作成されるため、重複チェックの方法を、時間制限で、
    302         *    同一メッセージの重複処理を行っています。
    303         *    よって、まったく異なる要因のエラーが同時に発生した場合、重複処理の判定で、除外される可能性が
    304         *    あります。（発生場所の行番号が異なれば、重複処理されても残るので、デバッグ可能です）
    305         *
    306         * @og.rev 6.4.3.2 (2016/02/19) 新規追加
    307         * @og.rev 6.9.2.1 (2018/03/12) 重複処理を、staticではなく、自身のオブジェクト内だけに変更します。
    308         * @og.rev 6.9.9.1 (2018/08/27) StringBuilder を、List に変更したが、empty時の処理漏れ対応。
    309         * @og.rev 7.0.7.2 (2019/12/28) ArrayList を、Collections.synchronizedList に変更します。
    310         */
    311        private static final class CaseBuilder {
    312                private static final String USE_KEY    = "USE_KEY" ;
    313                private static final long   CACHE_TIME = 3000L;                 // Exceptionが発生した場合、連続してCallされるため、一まとめにする。
    314
    315                private final ConcurrentMap<String,String> MSG_MAP = new ConcurrentHashMap<>(); // 同期Setの代わり。重複の取り除き判定
    316                private long lastCall ;                                                                 // 初期値 0L ( 6.9.9.4 (2018/10/01) PDM: Use of modifier volatile is not recommended.)
    317
    318//              private final List<String> list = new ArrayList<>();
    319                private final List<String> list = Collections.synchronizedList( new ArrayList<>() );            // 7.0.7.2 (2019/12/28)
    320
    321                /**
    322                 * デフォルトコンストラクタ。
    323                 *
    324                 * 時間制限が過ぎれば、キャッシュをクリアします。
    325                 *
    326                 * @og.rev 6.4.3.2 (2016/02/19) 新規追加
    327                 * @og.rev 6.4.3.3 (2016/03/04) synchronized を取り除きます。
    328                 * @og.rev 6.9.2.1 (2018/03/12) 重複処理を、staticではなく、自身のオブジェクト内だけに変更します。
    329                 * @og.rev 6.9.9.1 (2018/08/27) デフォルトコンストラクタに、super(); を呼び出すようにします。
    330                 */
    331                public CaseBuilder() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    332
    333                /**
    334                 * 初期化します。
    335                 *
    336                 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
    337                 * @og.rev 6.9.9.4 (2018/10/01) PDM: Use of modifier volatile is not recommended.
    338                 *
    339                 * @return   自分自身
    340                 * @og.rtnNotNull
    341                 */
    342                public CaseBuilder init() {
    343//                      synchronized( MSG_MAP ) {
    344                                final long now = System.currentTimeMillis();
    345                                if( now - lastCall > CACHE_TIME ) {                                     // 前回キャッシュ時からの経過時刻が、CACHE_TIME を上回っている場合。
    346                                        lastCall = now;
    347                                        MSG_MAP.clear();
    348                                        list.clear();
    349                                }
    350//                      }
    351                        return this;
    352                }
    353
    354                /**
    355                 * タイトルとメッセージを内部のStringBuilderに追記していきます。
    356                 *
    357                 * メッセージが、null や、空文字の場合は、何もしません。また、同一メッセージの追加は出来ません。
    358                 * 戻り値に、自分自身のオブジェクトを返すので、StringBuilder と同様に、接続できます。
    359                 *
    360                 * @og.rev 6.4.3.2 (2016/02/19) 新規追加
    361                 * @og.rev 6.4.3.3 (2016/03/04) 同一メッセージの判定を、trim() したキーで行います。
    362                 * @og.rev 6.9.2.1 (2018/03/12) タイトルがnullや空文字の場合も、なにもしません。
    363                 *
    364                 * @param    title      タイトルに相当します。
    365                 * @param    msg        メッセージ
    366                 * @return   自分自身
    367                 * @og.rtnNotNull
    368                 */
    369                public CaseBuilder append( final String title , final String msg ) {
    370                        if( title != null && !title.isEmpty() && msg != null && !msg.isEmpty() ) {
    371                                // 超特殊処理1
    372                                // msg に改行ｺｰﾄﾞを含む場合、最初の改行で、前後に分けて、それぞれをキャッシュ判定します。
    373                                // SQL文など、行が多く出る場合に、何度も繰り返されるのを避けるためです。
    374                                // 通常、１行目は、Exception発生元のため、異なるケースがありますが、２行目以降は、
    375                                // 同じケースが多いための処置です。
    376                                final String msg0 = msg.trim();
    377
    378                                // Map#putIfAbsent ： 戻り値は、以前の値。追加有り、置換なし(先勝)、削除なし
    379                                // Map#put と何が違うかというと、置き換えが無い。速度は誤差範囲です。
    380                                if( MSG_MAP.putIfAbsent( msg0,USE_KEY ) == null ) {             // 同期Setの代わり。未登録時は、null が戻る。
    381                                        final int adrs = msg0.indexOf( '\n' );                          // よくない判定方法
    382                                        if( adrs < 0 ) {
    383                                                list.add( title + msg0 );
    384                                        }
    385                                        else {
    386                                                final String msg1 = msg0.substring( 0,adrs ).trim();            // 最初の改行で、前後に分割します。
    387                                                final String msg2 = msg0.substring( adrs+1 ).trim();
    388                                                if( MSG_MAP.putIfAbsent( msg1,USE_KEY ) == null ) {
    389                                                        list.add( title + msg1 );
    390                                                }
    391                                                if( MSG_MAP.putIfAbsent( msg2,USE_KEY ) == null ) {
    392                                                        list.add( title + msg2 );
    393                                                }
    394                                        }
    395                                }
    396                        }
    397                        return this;
    398                }
    399
    400                /**
    401                 * Throwable の getStackTrace() 結果の内、opengion に関する箇所だけのStackTraceElement配列をCaseBuilderオブジェクトに書き込みます。
    402                 *
    403                 * 通常、スタックトレース情報は、膨大なメッセージが含まれるため、その中の、org.opengion か、org.apache.jsp.jsp を
    404                 * 含む箇所だけを、抜粋します。
    405                 * ただし、ThrowUtilクラス(このクラス)内で、スタックトレースを new しているため、このクラスで発生した
    406                 * スタックトレースは、含まないようにしています。このクラスからエラーが発生しないことを望みます。
    407                 * 処理は、先頭から、MIN_STACK_SIZE 行は、元のままのメッセージを出力し、minCnt件数で、打ち切ります。
    408                 * minCnt が、0 か、マイナスの場合は、打ち切り制限なしです。( Integer.MAX_VALUE を指定させるのが嫌だっただけです。 )
    409                 *
    410                 * @og.rev 6.4.2.0 (2016/01/29) 新規作成
    411                 * @og.rev 6.9.2.1 (2018/03/12) 選別だけを別に用意します。
    412                 *
    413                 * @param    th         元のThrowableオブジェクト(!= null 保障済み)
    414                 * @param    minCnt     StackTraceElementを登録する最小件数
    415                 * @return   自分自身
    416                 * @og.rtnNotNull
    417                 * @see         java.lang.Throwable#getStackTrace()
    418                 */
    419                public CaseBuilder addStackTrace( final Throwable th , final int minCnt ) {
    420                        for( final StackTraceElement stEle : ThrowUtil.selectElement( th,minCnt ) ) {
    421                                if( stEle == null || "".equals( stEle.getClassName() ) ) {
    422                                        append( "    at   " , "...." );                                 // StackTraceElement が省略されている。
    423                                }
    424                                else {
    425                                        append( "    at   " , stEle.toString() );
    426                                }
    427                        }
    428                        return this;
    429                }
    430
    431                /**
    432                 * 内部のStringBuilderを、文字列に変換して返します。
    433                 *
    434                 * 出力の直前に改行コードを出しています。
    435                 *
    436                 * @og.rev 6.4.3.2 (2016/02/19) 新規追加
    437                 * @og.rev 6.9.9.1 (2018/08/27) StringBuilder を、List に変更したが、empty時の処理漏れ対応。
    438                 * @og.rev 7.0.5.0 (2019/09/09) StringBuilder#append を、String.join に変更。
    439                 * @og.rev 7.0.7.2 (2019/12/28) ArrayList を、Collections.synchronizedList に変更します。
    440                 *
    441                 * @return   内部のStringBuilderの文字列化されたもの
    442                 * @og.rtnNotNull
    443                 */
    444                @Override
    445                public String toString() {
    446//                      synchronized( list ) {
    447                                return CR + String.join( CR,list );
    448//                      }
    449                }
    450
    451                /**
    452                 * 内部のStringBuilderを、文字列に変換して返します。
    453                 *
    454                 * 出力の直前に改行コードを出しています。
    455                 *
    456                 * @og.rev 6.4.3.2 (2016/02/19) 新規追加
    457                 * @og.rev 6.9.9.1 (2018/08/27) StringBuilder を、List に変更したが、empty時の処理漏れ対応。
    458                 * @og.rev 7.0.5.0 (2019/09/09) synchronized しておきます。
    459                 * @og.rev 7.0.7.2 (2019/12/28) ArrayList を、Collections.synchronizedList に変更します。
    460                 *
    461                 * @return   内部のStringBuilderの文字列化されたもの
    462                 * @og.rtnNotNull
    463                 */
    464                public String toThrowMsg() {
    465                        if( list.isEmpty() ) { return CR; }                     // 6.9.9.1 (2018/08/27)
    466
    467//                      synchronized( list ) {
    468                                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    469                                for( final String msg : list ) {
    470                                        if( ! msg.startsWith( "    at   " ) ) {
    471                                                buf.append( CR ).append( msg );
    472                                        }
    473                                }
    474                                return buf.toString();
    475//                      }
    476                }
    477        }
    478}