    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 org.jfree.chart.renderer.category.LineAndShapeRenderer;
    019import org.jfree.chart.renderer.category.CategoryItemRendererState;
    020import org.jfree.ui.RectangleEdge;
    021
    022import java.awt.Graphics2D;
    023import java.awt.Shape;
    024import java.awt.Color;
    025import java.awt.Paint;
    026import java.awt.Stroke;
    027import java.awt.geom.Line2D;
    028import java.awt.geom.Rectangle2D;
    029import java.io.IOException;
    030import java.io.ObjectOutputStream;
    031import java.io.ObjectInputStream;
    032
    033import org.jfree.chart.axis.CategoryAxis;
    034import org.jfree.chart.axis.ValueAxis;
    035import org.jfree.chart.entity.EntityCollection;
    036import org.jfree.chart.plot.CategoryPlot;
    037import org.jfree.chart.plot.PlotOrientation;
    038import org.jfree.data.category.CategoryDataset;
    039import org.jfree.data.general.DatasetUtilities;
    040import org.jfree.data.Range;
    041import org.jfree.util.ShapeUtilities;
    042
    043import java.awt.geom.AffineTransform;
    044
    045/**
    046 * HybsLineRenderer は、org.jfree.chart.renderer.category.LineAndShapeRenderer を
    047 * 拡張したカスタマイズクラスです。
    048 * これは、描画に対して、予め制限を設けて、処理速度の向上を図っています。
    049 *
    050 * @og.rev 3.8.9.2 (2007/07/28) 新規作成
    051 *
    052 * @version  0.9.0  2001/05/05
    053 * @author   Kazuhiko Hasegawa
    054 * @since    JDK1.1,
    055 */
    056public class HybsLineRenderer extends LineAndShapeRenderer implements HybsDrawItem {
    057        private static final long serialVersionUID = 519020100801L ;
    058
    059        private transient ValueMarkOverColors overColors = null;        // 4.0.3.0 (2008/01/07) マーカーラインでShapeを切り替える時の色指定
    060
    061        private Color[] shapeColors      = null;                // 4.0.3.0 (2008/01/07) データ毎にShapeを切り替える時の色指定
    062        private double  visibleLimit = Double.NEGATIVE_INFINITY;
    063        private int             dynamicOCNo  = -1;                      // 4.1.1.0 (2008/02/04) 動的なマーカーラインの基準シリーズ番号
    064        private String  shapeScale       = null;                // 4.1.1.0 (2008/02/04) shapeの大きさの倍率
    065        private boolean isItemLabelLastVisible = false; // 4.1.2.0 (2008/03/12)
    066        private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;               // 4.3.1.1 (2008/08/23)
    067
    068        /**
    069         * Creates a renderer with both lines and shapes visible by default.
    070         */
    071        public HybsLineRenderer() {
    072                super(true, true);
    073        }
    074
    075        /**
    076         * Creates a new renderer with lines and/or shapes visible.
    077         *
    078         * @param lines  draw lines?
    079         * @param shapes  draw shapes?
    080         */
    081        public HybsLineRenderer( final boolean lines, final boolean shapes ) {
    082                super(lines,shapes);
    083        }
    084
    085        /**
    086         * データ毎にShapeを切り替える時の色の繰返しパターンを文字列配列で指定します。
    087         *
    088         * HybsLine でのみ使用可能です。
    089         * これは、データそのものが、繰返し性のある場合に、その繰返し性に対応した
    090         * 形状のShape を表示させる場合に使用します。
    091         * 繰返しShapeの形状は、JFreeChart のシリーズ毎の繰返し標準形状を使用します。
    092         * 現在のバージョンでは、１０個までの繰返しに対応可能です。
    093         * 繰返し色を、指定した分だけ、順に使用されていきます。
    094         *
    095         * 指定文字列は、java.awt.Color クラスのstatic フィールド名で指定します。
    096         * BLACK , BLUE , CYAN , DARK_GRAY , GRAY , GREEN , LIGHT_GRAY ,
    097         * MAGENTA , ORANGE , PINK , RED , WHITE , YELLOW , (PURPLE) が指定できます。
    098         * また、#XXXXXX形式の16bitRGB表記 でも指定可能です。
    099         *
    100         * @og.rev 4.0.3.0 (2008/01/07) 新規追加
    101         *
    102         * @param       colors  データ毎の色の繰返しパターン(文字列配列)
    103         * @see         java.awt.Color#BLACK
    104         */
    105        protected void setShapeColors( final Color[] colors ) {
    106                shapeColors = colors;
    107        }
    108
    109        /**
    110         * shapeの大きさを倍率指定で変更します(初期値:null)。
    111         *
    112         * ラインチャートのShape(各グラフのポイントのマーカー)の大きさは、通常は、
    113         * 自動設定されます。
    114         * この大きさを、倍率指定で、変更可能です。
    115         * 指定は、double 型です。
    116         * 初期値は、null は、スケール変更しません(自動設定のままの大きさ)
    117         *
    118         * @og.rev 4.1.1.0 (2008/02/04) 新規追加
    119         *
    120         * @param       scale   shapeの大きさの倍率
    121         */
    122        protected void setShapeScale( final String scale ) {
    123                shapeScale = scale;
    124        }
    125
    126        /**
    127         * マーカーラインの超過時のShape色管理クラスを設定します。
    128         *
    129         * 動的なマーカーラインを使用する場合は、引数のシリーズデータが
    130         * マーカーラインの最下位閾値に相当します。これは、グラフ化されますが、
    131         * Shape は自動的に削除されます。
    132         * 逆に、最上位のデータ(シリーズ＝０)のShape は必ず付けます。
    133         *
    134         * @og.rev 4.1.0.1(2008/01/19) 新規追加
    135         * @og.rev 4.1.1.0 (2008/02/04) 動的なオーバーカラー
    136         *
    137         * @param       vmoc    マーカーラインの超過時のShape色管理クラス
    138         * @param       dynamicOverColorNo      動的なマーカーラインの基準シリーズ番号
    139         */
    140        protected void setValueMarkOverColors( final ValueMarkOverColors vmoc,
    141                                                                                final int dynamicOverColorNo ) {
    142                overColors      = vmoc;
    143                dynamicOCNo     = dynamicOverColorNo;
    144        }
    145
    146        /**
    147         * 表示下限値(これ以下のデータは未表示)の値(double)を指定します。
    148         *
    149         * HybsLine でのみ使用可能です。
    150         * この設定値以下のデータは、存在しない扱いとします。
    151         * Lineを引くとき、このデータと、存在しているデータ間にラインは引かれません。
    152         * 何も指定しない場合は、設定しません。
    153         *
    154         * @og.rev 4.0.3.0 (2008/01/07) 新規追加
    155         *
    156         * @param       limit   表示下限値(これ以下のデータは未表示)
    157         */
    158        protected void setVisibleLimit( final double limit ) {
    159                visibleLimit = limit;
    160        }
    161
    162        /**
    163         * itemLabelVisible 時に、最後の値のみ表示するかどうか[true:有効/false:無効]を指定します。
    164         *
    165         * これは、itemLabelVisible 属性に、"last" という設定値を指定した場合は、
    166         * 最後のみラベル表示します。
    167         * このメソッドでは、true が指定された場合は、"last" 属性が有効になったと
    168         * 判断します。
    169         *
    170         * @og.rev 4.1.2.0 (2008/03/12) 新規追加
    171         *
    172         * @param       flag    最後の値のみ表示するかどうか[true:有効/false:無効]
    173         */
    174        public void setItemLabelLastVisible( final boolean flag ) {
    175                isItemLabelLastVisible = flag;
    176        }
    177
    178        /**
    179         * drawItem と同等の機能を持った、高速版メソッドです。
    180         *
    181         * @og.rev 4.0.3.0 (2008/01/07) shapeColors 属性追加
    182         * @og.rev 4.1.1.0 (2008/02/04) 繰返しshapeの開始番号(shapeStartNo)追加
    183         * @og.rev 4.1.1.0 (2008/02/04) seriesColors 属性は、色(Paint)のみ切り替えるようにする。
    184         * @og.rev 4.1.1.0 (2008/02/04) ラベルブレイク機能の追加(HybsCategoryAxis)
    185         * @og.rev 4.1.1.0 (2008/02/04) 動的なマーカーライン
    186         * @og.rev 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加
    187         * @og.rev 4.1.2.0 (2008/03/12) ラベルのアンダーライン時にItemLavelを表示しない
    188         *
    189         * @param g2                    Graphics2Dオブジェクト
    190         * @param state                 CategoryItemRendererStateオブジェクト
    191         * @param dataArea              Rectangle2Dオブジェクト
    192         * @param plot                  CategoryPlotオブジェクト
    193         * @param domainAxis    CategoryAxisオブジェクト
    194         * @param rangeAxis             ValueAxisオブジェクト
    195         * @param dataset               CategoryDatasetオブジェクト
    196         * @param serNo                 シリアル番号
    197         */
    198        public void drawItem2( final Graphics2D g2, final CategoryItemRendererState state,
    199                        final Rectangle2D dataArea, final CategoryPlot plot, final CategoryAxis domainAxis,
    200                        final ValueAxis rangeAxis, final CategoryDataset dataset, final int serNo ) {
    201
    202                int clmCount = dataset.getColumnCount();
    203                int rowCount = dataset.getRowCount();
    204                RectangleEdge domEdge   = plot.getDomainAxisEdge();
    205                RectangleEdge rangeEdge = plot.getRangeAxisEdge();
    206
    207                boolean isShape = getBaseShapesVisible() ;
    208
    209                HybsCategoryAxis hybsAxis = null;
    210                if( domainAxis instanceof HybsCategoryAxis ) {
    211                        hybsAxis = (HybsCategoryAxis)domainAxis;
    212                        hybsAxis.setItemLabelLastVisible( isItemLabelLastVisible );
    213                }
    214
    215                // データ毎にShapeを切り替える時の色の繰返しパターン
    216                int shpCnt = ( shapeColors == null ) ? 1 : shapeColors.length;
    217
    218                // Shape の形状を指定できる。任意ではなく、表示順の開始位置の指定
    219                int shapeNo = 0 ;
    220
    221                // shapeの大きさの倍率
    222                AffineTransform transform = null;
    223                if( shapeScale != null ) {
    224                        double scale = Double.parseDouble( shapeScale );
    225                        transform = AffineTransform.getScaleInstance( scale, scale );
    226                }
    227
    228                // 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加
    229                Stroke baseStroke = getBaseStroke();
    230                if( baseStroke != null ) { g2.setStroke( baseStroke ); }
    231
    232                // トリッキー：row == serNo を処理したいがために、１回余分にループをまわす。
    233                for( int row=0; row<=rowCount; row++ ) {
    234                        if( row == serNo ) { continue; }        // Mis Add 2007/07/23
    235                        if( row >= rowCount ) {
    236                                if( serNo >= 0 ) {
    237                                        row = serNo;
    238                                        rowCount = -1;          // 終了条件
    239                                }
    240                                else {
    241                                        break;
    242                                }
    243                        }
    244
    245                        // 4.1.1.0 (2008/02/22) Stroke を設定するロジックを追加
    246                        Stroke serStroke = getSeriesStroke( row );
    247                        if( serStroke != null ) { g2.setStroke( serStroke ); }
    248
    249                        Paint rowPaint = lookupSeriesPaint( row );
    250                        Shape rowShape = lookupSeriesShape( shapeNo );
    251
    252                        // shapeの大きさの倍率
    253                        if( transform != null ) {
    254                                rowShape = transform.createTransformedShape(rowShape);
    255                        }
    256
    257                        Paint linePaint = rowPaint ;
    258                        // 4.1.1.0 (2008/02/04) 動的なマーカーライン(row==dynamicOCNoのデータで判定)
    259                        boolean shapeFlag = false;
    260                        if( row == dynamicOCNo ) {      // 使わない場合は、-1 なので、マッチしない。
    261                                if( overColors != null ) {
    262                                        linePaint = overColors.getDynamicColor();
    263                                }
    264                        }
    265                        else {
    266                                // 動的なマーカーライン使用時(dynamicOCNo >= 0)は、row == 0 で、Shape を使います。
    267                                if( isShape || row == serNo || ( dynamicOCNo >= 0 && row == 0 ) ) {
    268                                        shapeFlag = true;
    269                                        shapeNo++ ;             // Shape 形状の変更は、使用した場合のみ
    270                                }
    271                        }
    272
    273                        double v0 = 0;
    274                        double x0 = 0;
    275                        double y0 = 0;
    276
    277                        boolean isItemLabelsVisible = isSeriesItemLabelsVisible( row );
    278
    279                        int clmSeq = 0;         // カラムの繰返し制御(Shape色の順番表示用)
    280                        for( int column=0; column<clmCount; column++ ) {
    281                                // nothing is drawn for null...
    282                                Number v1Num = dataset.getValue( row,column );
    283                                if(v1Num == null) { continue; }
    284                                double v1 = v1Num.doubleValue();
    285                                double x1 = domainAxis.getCategoryMiddle( column,clmCount,dataArea,domEdge );
    286                                double y1 = rangeAxis.valueToJava2D( v1,dataArea,rangeEdge );
    287
    288                                // Line の描画
    289                                if( column > 0 && v0 >= visibleLimit && v1 >= visibleLimit ) {
    290                                        Line2D line = new Line2D.Double( x0,y0,x1,y1 );
    291                                        g2.setPaint( linePaint );
    292                                        g2.draw( line );
    293                                }
    294
    295                                // Shape の描画
    296                                if( shapeFlag ) {
    297                                        // ラベルブレイク処理
    298                                        if( hybsAxis != null && hybsAxis.isLabelBreak( column ) ) {
    299                                                clmSeq = 0;             // 初期化
    300                                        }
    301
    302                                        int adrs = clmSeq%shpCnt;
    303                                        clmSeq++ ;
    304                                        Paint paint = ( shapeColors == null ) ? rowPaint : shapeColors[adrs];
    305                                        // 4.1.1.0 (2008/02/04) 動的なマーカーライン(row==dynamicOCNoのデータで判定)
    306                                        if( overColors != null ) {
    307                                                if( dynamicOCNo >= 0 ) {
    308                                                        paint = overColors.getColor( v1,dataset.getValue( dynamicOCNo,column ) );
    309                                                }
    310                                                else {
    311                                                        paint = overColors.getColor( v1 );
    312                                                }
    313                                        }
    314                                        g2.setPaint(paint);
    315                                        Shape shape = ShapeUtilities.createTranslatedShape( rowShape,x1,y1 );
    316                                        g2.fill(shape);
    317                                        g2.setPaint(rowPaint);  // 色を戻す。
    318                                        // 4.3.1.0 (2008/08/09) add an item entity, if this information is being collected
    319                                        EntityCollection entities = state.getEntityCollection();
    320                                        if( entities != null ) {
    321                                                addItemEntity( entities, dataset, row, column, shape );
    322                                        }
    323                                }
    324
    325                                // ItemLabel の描画
    326                                //   山形 0-1-0 nega=fale , x= 0 上中
    327                                //   右坂 0-1-2 nega=true , x=10 下右
    328                                //   谷形 1-0-1 nega=true , x= 0 下中
    329                                //   左坂 2-1-0 nega=fale , x=10 上右
    330                                if( isItemLabelsVisible ) {
    331                                        // 4.1.2.0 (2008/03/12) アンダースコアの場合は、表示しない。
    332                                        if( hybsAxis != null && hybsAxis.isViewItemLabel( column ) ) {
    333                                                double v2 = v0 ;        // 仮設定(最後のcolumnとnull の場合)
    334                                                if( column+1 < clmCount ) {
    335                                                        Number v2Num = dataset.getValue( row,column+1 );
    336                                                        if( v2Num != null ) {
    337                                                                v2 = v2Num.doubleValue();
    338                                                        }
    339                                                }
    340                                                boolean nega = ( v1<v2 ) ;
    341                                                double  lblx = ( nega && v0<v1 || !nega && v0>v1 ) ? x1 + 10 : x1 ;
    342
    343                                                drawItemLabel( g2,PlotOrientation.VERTICAL,dataset,row,column,lblx,y1,nega );
    344                                        }
    345                                }
    346
    347                                v0 = v1;        // Lineを消す処理に、過去の値が必要
    348                                x0 = x1;
    349                                y0 = y1;
    350                        }
    351                }
    352        }
    353
    354        /**
    355         * この文字列と指定されたオブジェクトを比較します。
    356         *
    357         * 親クラスで、equals メソッドが実装されているため、警告がでます。
    358         *
    359         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    360         *
    361         * @param object Object
    362         *
    363         * @return      Objectが等しい場合は true、そうでない場合は false
    364         */
    365//      @Override
    366//      public boolean equals( final Object object ) {
    367//              return super.equals( object );
    368//      }
    369
    370        /**
    371         * このオブジェクトのハッシュコードを取得します。
    372         *
    373         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    374         *
    375         * @return      ハッシュコード
    376         */
    377//      public int hashCode() { return super.hashCode() ; }
    378
    379        /**
    380         * このオブジェクトと指定されたオブジェクトを比較します。
    381         *
    382         * @og.rev 4.3.1.1 (2008/08/23) 新規追加
    383         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    384         *
    385         * @param       anObject        比較されるオブジェクト
    386         *
    387         * @return      指定されたオブジェクトが等しい場合は true、そうでない場合は false
    388         */
    389        @Override
    390        public boolean equals( final Object anObject ) {
    391//              return this == anObject;
    392                if( super.equals( anObject ) ) {
    393                        return hsCode == ((HybsLineRenderer)anObject).hsCode;
    394                }
    395                return false;
    396        }
    397
    398        /**
    399         * このオブジェクトのハッシュコードを返します。
    400         *
    401         * @og.rev 4.3.1.1 (2008/08/23) 新規追加
    402         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    403         *
    404         * @return      このオブジェクトのハッシュコード値
    405         */
    406        @Override
    407        public int hashCode() { return hsCode ; }
    408
    409        /**
    410         * 指定されたデータセットからのアイテムをすべて表示するために、要求する値の範囲を返します。
    411         *
    412         * @param       dataset カテゴリDataset
    413         *
    414         * @return      Rangeオブジェクト
    415         */
    416        @Override
    417        public Range findRangeBounds( final CategoryDataset dataset ) {
    418                if( dataset instanceof HybsJDBCCategoryDataset2 ) {
    419                        return ((HybsJDBCCategoryDataset2)dataset).getRange();
    420                }
    421                else {
    422                        return DatasetUtilities.findRangeBounds(dataset);
    423                }
    424        }
    425
    426        /**
    427         * シリアライズ用のカスタムシリアライズ書き込みメソッド
    428         *
    429         * @og.rev 4.1.0.1(2008/01/19) 新規追加
    430         * @serialData 一部のオブジェクト(ValueMarkOverColors)は、シリアライズされません。
    431         *
    432         * @param       strm    ObjectOutputStreamオブジェクト
    433         * @throws IOException 入出力エラーが発生した場合
    434         */
    435        private void writeObject( final ObjectOutputStream strm ) throws IOException {
    436                strm.defaultWriteObject();
    437        }
    438
    439        /**
    440         * シリアライズ用のカスタムシリアライズ読み込みメソッド
    441         *
    442         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
    443         *
    444         * @og.rev 4.1.0.1(2008/01/19) 新規追加
    445         * @serialData 一部のオブジェクト(ValueMarkOverColors)は、読み込まれません。
    446         *
    447         * @param       strm    ObjectInputStreamオブジェクト
    448         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
    449         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
    450         */
    451        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
    452                strm.defaultReadObject();
    453        }
    454}