    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.hayabusa.io;
    017
    018import java.util.List;
    019import java.util.ArrayList;
    020import java.awt.Graphics2D;
    021import java.awt.geom.Rectangle2D;
    022import org.jfree.ui.RectangleEdge;
    023import org.jfree.text.TextBlock;
    024import org.jfree.chart.axis.AxisState;
    025import org.jfree.chart.axis.CategoryAxis;
    026import org.jfree.chart.axis.CategoryAnchor;
    027
    028import org.opengion.fukurou.util.StringUtil;                                            // 6.2.0.0 (2015/02/27)
    029
    030/**
    031 * HybsCategoryAxis は、CategoryAxis クラスを継承した、横軸管理クラスです。
    032 * 横軸ラベルの表示制御を、主に行っています。
    033 * 横軸表示には、３つの制御機能がカスタマイズされています。
    034 *
    035 *  １．"_" ラベルのスキップ(非表示)
    036 *  ２．cutNo 属性による、ラベル文字位置指定のキーブレイク
    037 *  ３．skip 属性による、ラベルをスキップする間隔の指定
    038 *
    039 * 上記、１，２，３ の順番で優先的に処理されます。
    040 *
    041 * @version  0.9.0      2007/06/21
    042 * @author       Kazuhiko Hasegawa
    043 * @since        JDK1.1,
    044 */
    045public class HybsCategoryAxis extends CategoryAxis {
    046        private static final long serialVersionUID = 519020100801L ;
    047
    048        private static final TextBlock NULL_LABEL = new TextBlock() ;
    049
    050        /**
    051         * LabelVisible の状態を表す、enum 定義です。
    052         *
    053         * TRUE(true) , FALSE(false) , UNDER(true) を持っています。
    054         *
    055         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
    056         */
    057        private enum LabelVisible { TRUE(true) , FALSE(false) , UNDER(true) ;
    058                private final boolean flag ;
    059
    060                /**
    061                 * enumのコンストラクタ
    062                 *
    063                 * @param       flag    boolean型の値
    064                 */
    065                LabelVisible( final boolean flag ) { this.flag = flag; }
    066
    067                /**
    068                 * enum内部の値を、boolean 型に変換します。
    069                 *
    070                 * @return      boolean 型の値
    071                 */
    072                public boolean booleanValue() { return flag; }
    073        };
    074
    075        /** For serialization. */
    076        private int skip  = 1;                          // skip数
    077        private int count       ;                               // skip 時の現在位置のカウント
    078
    079        private transient List<LabelVisible> labelBreak ;
    080        private int             cutNo                   = -1;           // 4.1.1.0 (2008/02/04) ラベルブレイクのsubstring 位置
    081        private String  breakKey                ;                       // 4.1.1.0 (2008/02/04) ラベルブレイクの前回キー
    082        private boolean isLastVisible   ;                       // 4.1.2.0 (2008/03/12) 6.0.2.5 (2014/10/31) refactoring
    083        private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;       // 5.1.9.0 (2010/08/01) equals,hashCode
    084
    085        /**
    086         * 引数を指定して作成する コンストラクター
    087         *
    088         * skip(ラベルの表示間隔) = 1 , cutNo(ラベルブレイクのsubstring 位置) = -1 で初期化します。
    089         *
    090         * @param       label   ラベル
    091         */
    092        public HybsCategoryAxis( final String label ) {
    093                this( label,1,-1 );
    094        }
    095
    096        /**
    097         * 引数を指定して作成する コンストラクター
    098         *
    099         * @og.rev 4.1.1.0 (2008/02/04) cutNo 新規追加
    100         *
    101         * @param       label   ラベル
    102         * @param       skip    ラベルの表示間隔
    103         * @param       cutNo   ラベルブレイクのsubstring 位置
    104         */
    105        protected HybsCategoryAxis( final String label,final int skip,final int cutNo ) {
    106                super( label );
    107                this.skip       = skip ;
    108                this.cutNo      = cutNo ;
    109        }
    110
    111        /**
    112         * itemLabelVisible 時に、最後の値のみ表示するかどうか[true/false]を指定します。
    113         *
    114         * これは、itemLabelVisible 属性に、"last" という設定値を指定した場合は、
    115         * 最後のみラベル表示します。
    116         * このメソッドでは、true が指定された場合は、"last" 属性が有効になったと
    117         * 判断します。
    118         * (独自メソッド)
    119         *
    120         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
    121         *
    122         * @param       flag            最後の値のみ表示するかどうか[true/false]
    123         */
    124        protected void setItemLabelLastVisible( final boolean flag ) {
    125                isLastVisible = flag;                                   // 6.0.2.5 (2014/10/31) refactoring
    126        }
    127
    128        /**
    129         * 軸を引く場合、使用することができるチックの一時的リストを作成します。
    130         *
    131         * @og.rev 4.1.1.0 (2008/02/04) labelBreak 新規追加
    132         *
    133         * @param       g2                      Graphics2Dオブジェクト(フォント測定に使用)
    134         * @param       state           AxisStateオブジェクト
    135         * @param       dataArea        インサイドエリアを示すRectangle2Dオブジェクト
    136         * @param       edge            ロケーションを指定するRectangleEdgeオブジェクト
    137         *
    138         * @return      チックのリスト
    139         */
    140        @Override
    141        public List<?> refreshTicks( final Graphics2D g2,
    142                                                          final AxisState state,
    143                                                          final Rectangle2D dataArea,
    144                                                          final RectangleEdge edge) {
    145                count = 0;
    146                labelBreak = new ArrayList<>();
    147
    148                return super.refreshTicks( g2, state, dataArea, edge);
    149        }
    150
    151        /**
    152         * TextBlock オブジェクトを作成します。
    153         *
    154         * このメソッドでは、３つの拡張機能を実現しています。
    155         *  １．"_" ラベルのスキップ(非表示)
    156         *  ２．cutNo 属性による、ラベル文字位置指定のキーブレイク
    157         *  ３．skip 属性による、ラベルをスキップする間隔の指定
    158         *  ４．改行コード(chr(10)、￥n)または、改行コード文字列('￥n'文字列)による、ラベルの段組指定 (6.4.9.2 (2016/08/19))
    159         * cutNo が指定された場合は、skip 処理は行われません。また、
    160         * その場合のラベルは、cutNoで指定された先頭文字列のみ表示されます。
    161         * 文字列が、cutNoで指定された数より小さい場合は、そのまま使用されます。
    162         *
    163         * @og.rev 4.1.1.0 (2008/02/04) cutNo,labelBreak 追加
    164         * @og.rev 4.1.2.0 (2008/03/12) LabelVisible.UNDER 処理を追加
    165         * @og.rev 4.3.1.1 (2008/08/23) lbl の null参照はずしの対応
    166         * @og.rev 6.4.9.2 (2016/08/19) 横軸ラベルの折り返し対応
    167         *
    168         * @param       category        カテゴリ名
    169         * @param       width           幅
    170         * @param       edge            表示範囲を示すRectangleEdgeオブジェクト
    171         * @param       g2                      Graphics2Dオブジェクト
    172         *
    173         * @return      TextBlockオブジェクト
    174         */
    175        @SuppressWarnings("rawtypes")
    176        @Override
    177        protected TextBlock createLabel( final Comparable category, final float width, final RectangleEdge edge, final Graphics2D g2 ) {
    178                TextBlock label = null ;
    179                String    lbl   = null;
    180                if( category instanceof String ) {                                      // 4.3.1.1 (2008/08/23) instanceof チェックは、nullチェック不要
    181                        lbl = (String)category;
    182                        if( StringUtil.startsChar( lbl , '_' ) ) {              // 6.2.0.0 (2015/02/27) １文字 String.startsWith
    183                                label = NULL_LABEL;
    184                        }
    185                }
    186
    187                if( cutNo > 0 && lbl != null ) {
    188                        if( lbl.length() >= cutNo ) {
    189                                lbl = lbl.substring( 0,cutNo );
    190                        }
    191
    192                        if( ! lbl.equals( breakKey ) ) {
    193                                // 6.4.9.2 (2016/08/19) 横軸ラベルの折り返し対応
    194                                label = multiLabel( lbl, width, edge,  g2 );
    195                                breakKey = lbl ;
    196                        }
    197                }
    198                else {
    199                        if( count % skip == 0 ) {
    200                                // 6.4.9.2 (2016/08/19) 横軸ラベルの折り返し対応
    201                                label = multiLabel( category, width, edge,  g2 );
    202                        }
    203                        count++;
    204                }
    205
    206                if( label == null ) {
    207                        label = NULL_LABEL;
    208                        labelBreak.add( LabelVisible.FALSE );
    209                }
    210                else if( label.equals( NULL_LABEL ) ) {
    211                        labelBreak.add( LabelVisible.UNDER );
    212                }
    213                else {
    214                        labelBreak.add( LabelVisible.TRUE );
    215                }
    216
    217                return label;
    218        }
    219
    220        /**
    221         * 複数行の横軸に対応した、TextBlock オブジェクトを作成します。
    222         *
    223         * ラベルに改行コードを含む場合、ラベルを段組(改行)します。
    224         * 改行コードとは、本当の改行コード(ORACLEで言うところの、chr(10)か、
    225         * 改行コード文字列('￥n'文字列)を認識します。
    226         *
    227         * @og.rev 6.4.9.2 (2016/08/19) 横軸ラベルの折り返し対応
    228         *
    229         * @param       category        カテゴリ名(ラベル)
    230         * @param       width           幅
    231         * @param       edge            表示範囲を示すRectangleEdgeオブジェクト
    232         * @param       g2                      Graphics2Dオブジェクト
    233         *
    234         * @return      TextBlockオブジェクト
    235         */
    236        @SuppressWarnings("rawtypes")
    237        private TextBlock multiLabel( final Comparable category, final float width, final RectangleEdge edge, final Graphics2D g2 ) {
    238                final TextBlock label ;
    239                // splitの関係上、文字列のみ処理します。
    240                if( category instanceof String ) {
    241                        final String lbl = (String)category;
    242                        final String[] sp = lbl.split( "\\n|\\\\n" );
    243                        label = super.createLabel( sp[0], width, edge,  g2 );           // 一つ目のラベル
    244                        for( int i=1; i<sp.length; i++ ) {                                                      // 改行があれば、ここでループされる。
    245                                final TextBlock lbl2 = super.createLabel( sp[i], width, edge,  g2 );
    246                                label.addLine( lbl2.getLastLine() );                                    // フォント情報などを引き継ぐ
    247                        }
    248                }
    249                else {
    250                        label = super.createLabel( category, width, edge,  g2 );        // 文字列でなければ、そのまま使用する。
    251                }
    252
    253                return label;
    254        }
    255
    256        /**
    257         * ラベルブレイクするかどうかを返します。
    258         *
    259         * skip または、cutNo によるラベルの間引き処理で、指定のCategoryAxis
    260         * に対するカラム番号を指定する事で、判定値を返します。
    261         * 処理が、Label の作成済みかどうかに依存する為、その判定を先に行います。
    262         *
    263         * @og.rev 4.1.1.0 (2008/02/04) 新規追加
    264         *
    265         * @param column カラム番号
    266         *
    267         * @return      ラベルブレイクするかどうか(true:する)
    268         */
    269        protected boolean isLabelBreak( final int column ) {
    270                return labelBreak == null ||
    271                                labelBreak.size() <= column ||
    272                                labelBreak.get( column ).booleanValue() ;
    273        }
    274
    275        /**
    276         * ITEM ラベル(各データの設定値の説明用の値)を表示するかどうかを返します。
    277         *
    278         * ラベルの先頭に、アンダースコアがついたラベルは、ラベルの表示と
    279         * ItemLabel の表示を抑止します。(false)
    280         * それ以外のラベルは、表示する(true) を返します。
    281         * 処理が、Label の作成済みかどうかに依存する為、その判定を先に行います。
    282         *
    283         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
    284         *
    285         * @param       column  カラム番号
    286         *
    287         * @return      ITEMラベルを表示するかどうか(true:する)
    288         */
    289        protected boolean isViewItemLabel( final int column ) {
    290                boolean flag = labelBreak == null ||
    291                                                labelBreak.size() <= column ||
    292                                                labelBreak.get( column ) != LabelVisible.UNDER ;
    293
    294                if( flag && isLastVisible && labelBreak.size() -1 != column ) {                 // 6.0.2.5 (2014/10/31) refactoring
    295                        flag = false;
    296                }
    297
    298                return flag;
    299        }
    300
    301        /**
    302         * ドメイン(横軸)のカテゴリ単位のライン(縦線)の描画位置を返します。
    303         *
    304         * この位置は、labelBreak が存在しないか、または、ブレークするときのみ
    305         * 値を返します。これにより、ライン(縦線)の位置を、グラフの中心から
    306         * ずらす事が可能になります。
    307         * また、labelBreak により、ラベルを描画しない場合は、線の位置を、０ に
    308         * 設定する事で、画面から見えなくします。
    309         *
    310         * @param       anchor                  CategoryAnchorオブジェクト
    311         * @param       category                カテゴリ番号
    312         * @param       categoryCount   カテゴリ数
    313         * @param       area                    範囲を表すRectangle2Dオブジェクト
    314         * @param       edge                    ロケーションを指定するRectangleEdgeオブジェクト
    315         *
    316         * @return      ライン(縦線)の描画位置
    317         */
    318        @Override
    319        public double getCategoryJava2DCoordinate( final CategoryAnchor anchor,
    320                                                                                           final int category,
    321                                                                                           final int categoryCount,
    322                                                                                           final Rectangle2D area,
    323                                                                                           final RectangleEdge edge) {
    324
    325                final double result ;
    326
    327                // labelBreak が存在しないか、または、ブレークするときのみ値を返す。
    328                if( isLabelBreak( category ) ) {
    329                        result = super.getCategoryJava2DCoordinate(
    330                                                        anchor,category,categoryCount,area,edge
    331                                        ) ;
    332                }
    333                else {
    334                        result = 0;
    335                }
    336                return result ;
    337        }
    338
    339        /**
    340         * この文字列と指定されたオブジェクトを比較します。
    341         *
    342         * 親クラスで、equals メソッドが実装されているため、警告がでます。
    343         *
    344         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    345         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    346         *
    347         * @param       object  比較するオブジェクト
    348         *
    349         * @return      Objectが等しい場合は true、そうでない場合は false
    350         */
    351        @Override
    352        public boolean equals( final Object object ) {
    353                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
    354                return super.equals( object ) && hsCode == ((HybsCategoryAxis)object).hsCode;
    355
    356        }
    357
    358        /**
    359         * このオブジェクトのハッシュコードを取得します。
    360         *
    361         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    362         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    363         *
    364         * @return      ハッシュコード
    365         */
    366        @Override
    367        public int hashCode() { return hsCode ; }
    368}