    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.awt.Graphics2D;
    019import java.awt.Stroke;
    020import java.awt.Paint;
    021import java.awt.Color;
    022import java.awt.geom.Rectangle2D;
    023import java.util.List;
    024import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
    025import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
    026import java.util.Iterator;
    027
    028import org.jfree.chart.plot.CategoryPlot;
    029import org.jfree.chart.plot.PlotRenderingInfo;
    030import org.jfree.chart.plot.CategoryCrosshairState;
    031import org.jfree.chart.urls.CategoryURLGenerator;
    032import org.jfree.chart.axis.CategoryAxis;
    033import org.jfree.chart.axis.ValueAxis;
    034import org.jfree.chart.axis.ValueTick;
    035import org.jfree.chart.renderer.category.CategoryItemRenderer;
    036import org.jfree.chart.renderer.category.CategoryItemRendererState;
    037import org.jfree.chart.renderer.category.StackedBarRenderer;
    038import org.jfree.data.category.CategoryDataset;
    039import org.jfree.data.general.DatasetUtilities;
    040
    041/**
    042 * HybsCategoryPlot は、CategoryPlot を拡張したカスタマイズクラスです。
    043 * これは、シリーズの表示色を変更する箇所で、描画順により、きれいに表示されない
    044 * 現象への対応案です。
    045 * 描画順を、最も最後に行うように、修正しています。
    046 *
    047 * renders each data item using a {@link CategoryItemRenderer}.
    048 *
    049 * @og.rev 3.8.9.2 (2007/07/28) 新規作成
    050 *
    051 * @version  0.9.0  2001/05/05
    052 * @author   Kazuhiko Hasegawa
    053 * @since    JDK1.1,
    054 */
    055public class HybsCategoryPlot extends CategoryPlot {
    056        private static final long serialVersionUID = 602020140919L ;
    057
    058        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
    059        private final ConcurrentMap<Integer,Double> barWdMap = new ConcurrentHashMap<>();       // 6.4.3.1 (2016/02/12)
    060        private int             serNo     = -1;
    061        private int             rangeSkip = -1;                 // 4.1.1.0 (2008/02/04) 縦軸のグリッドをスキップする間隔(初期値:-1)
    062        private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;       // 5.1.9.0 (2010/08/01) equals,hashCode
    063
    064        /**
    065         * デフォルトコンストラクター
    066         * シリーズ番号を、初期化しておきます。
    067         *
    068         **/
    069        public HybsCategoryPlot() {
    070                // 4.3.4.4 (2009/01/01)
    071                this( -1 );
    072        }
    073
    074        /**
    075         * シリーズ番号 を、指定して、オブジェクトを作成するコンストラクター
    076         *
    077         * @param       serNo   ピックアップするシリーズ番号
    078         **/
    079        protected HybsCategoryPlot( final int serNo ) {
    080                super();                // 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
    081                // 4.3.4.4 (2009/01/01)
    082                this.serNo = serNo;
    083        }
    084
    085        /**
    086         * ピックアップするシリーズ番号を設定します(独自メソッド)。
    087         *
    088         * @og.rev 4.1.1.0 (2008/02/04) データの値(itemText)表示の継承
    089         *
    090         * @param       newSerNo        ピックアップするシリーズ番号
    091         **/
    092        protected void setSeriesPikup( final int newSerNo ) {
    093                final int oldSerNo = serNo ;
    094                serNo = newSerNo;
    095                final CategoryItemRenderer rend = getRenderer();
    096                final CategoryURLGenerator urlGen = rend.getSeriesItemURLGenerator( oldSerNo );
    097                if( urlGen != null ) {
    098                        rend.setSeriesItemURLGenerator( oldSerNo,null );
    099                        rend.setSeriesItemURLGenerator( serNo   ,urlGen );
    100                }
    101
    102                // 4.1.1.0 (2008/02/04) データの値(itemText)表示の継承
    103                if( rend.isSeriesItemLabelsVisible( oldSerNo ) ) {
    104                        rend.setSeriesItemLabelsVisible( oldSerNo,false );
    105                        rend.setSeriesItemLabelsVisible( serNo   ,true  );
    106                }
    107        }
    108
    109        /**
    110         * 縦軸のグリッド線(水平線)をスキップする間隔を指定します(初期値:-1)。
    111         *
    112         * 縦軸のグリッド線を表示する際に、スキップする間隔を指定します。
    113         * 通常は、ラベルと同じだけのグリッド線が掛かれますが、ラベルよりも
    114         * 少ない数のグリッド線(例えば、２つおき)を出す場合に、値を設定します。
    115         * "1" (初期値)では、１つづつ表示(つまり、すべて表示する)します。
    116         * "2" とすると、１つおきに、"3" とすると、２つおきに表示します。
    117         * なお、先頭から表示を開始します。
    118         * (独自メソッド)
    119         *
    120         * 6.0.2.0 (2014/09/19) 前回の JFreeChart のVerUpで、グリッド線の表示が
    121         * ５個おきに変わったようです。設定する値を マイナスにすると、初期設定の値を
    122         * 使用するように変更します。微調整は、個々にお願いします。
    123         *
    124         * 初期値は、"-1" (設定しない)です。
    125         *
    126         * @og.rev 4.1.1.0 (2008/02/04) 新規追加
    127         *
    128         * @param       rngSkip 縦軸のグリッド線(水平線)をスキップする間隔
    129         */
    130        protected void setRangeSkip( final int rngSkip ) {
    131                rangeSkip = rngSkip;
    132        }
    133
    134        /**
    135         * BarChart のバーの幅を直接指定します。
    136         * 通常は、maxBarWidth や itemMargin で比率指定しますが、
    137         * ここでは、CategoryItemRendererState オブジェクトに直接設定する為の
    138         * データセット単位のマップを作成します。
    139         * (独自メソッド)
    140         *
    141         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
    142         *
    143         * @param       index   データセット番号
    144         * @param       width   バーの幅
    145         **/
    146        protected void setBarWidth( final int index,final Double width ) {
    147                // 6.4.3.1 (2016/02/12) ConcurrentMap 系は、key,val ともに not null 制限です。
    148                if( width != null ) { barWdMap.put( index,width ); }
    149        }
    150
    151        /**
    152         * CategoryPlot の render メソッドをオーバーライドしています。
    153         *
    154         * Draws a representation of a dataset within the dataArea region using the
    155         * appropriate renderer.
    156         *
    157         * @param g2  the graphics device.
    158         * @param dataArea      the region in which the data is to be drawn.
    159         * @param index  the dataset and renderer index.
    160         * @param info  an optional object for collection dimension information.
    161     * @param crosshairState  a state object for tracking crosshair info (null permitted).
    162         *
    163         * @return      描画するデータが見つかった場合は、true
    164         */
    165        @Override
    166        public boolean render( final Graphics2D g2, final Rectangle2D dataArea, final int index,
    167                                                  final PlotRenderingInfo info , final CategoryCrosshairState crosshairState) {
    168
    169                boolean foundData = false;
    170                final CategoryDataset currentDataset = getDataset(index);
    171                final CategoryItemRenderer renderer = getRenderer(index);
    172                // 4.3.1.0 (2008/08/19) 軸とデータセットのマッピング
    173                this.mapDatasetToRangeAxis( index, index );
    174
    175                CategoryAxis domainAxis = null ;
    176                if( renderer instanceof StackedBarRenderer ) {
    177                        domainAxis = getDomainAxis(index);
    178                }
    179                else {
    180                        domainAxis = getDomainAxisForDataset(index);
    181                }
    182
    183                final ValueAxis rangeAxis = getRangeAxis(index);
    184                final boolean hasData = !DatasetUtilities.isEmptyOrNull(currentDataset);
    185                if( hasData && renderer != null ) {
    186                        foundData = true;
    187                        final CategoryItemRendererState state = renderer.initialise(g2, dataArea, this, index, info);
    188
    189                        // 4.0.3.0 (2008/01/07) 棒グラフのバー幅指定
    190                        final Double bwidth = barWdMap.get( index );
    191                        if( bwidth != null ) { state.setBarWidth( bwidth.doubleValue() ); }
    192
    193                        if( renderer instanceof HybsDrawItem ) {
    194                                // 6.0.2.1 (2014/09/26) categoryカラー配列
    195                                if( currentDataset instanceof HybsDataset ) {
    196                                        final Color[] categoryColor = ((HybsDataset)currentDataset).getCategoryColor();
    197                                        ((HybsDrawItem)renderer).setCategoryColor( categoryColor );
    198                                }
    199
    200                                ((HybsDrawItem)renderer).drawItem2(g2, state, dataArea, this,
    201                                                domainAxis, rangeAxis, currentDataset , serNo );
    202                        }
    203                        else {
    204                                final int clmCount = currentDataset.getColumnCount();
    205                                final int rowCount = currentDataset.getRowCount();
    206                                final int passCount = renderer.getPassCount();
    207                                for( int pass=0; pass<passCount; pass++ ) {
    208                                        for( int column=0; column<clmCount; column++ ) {
    209                                                for( int row=0; row<rowCount; row++ ) {
    210                                                        if( row == serNo ) { continue; }        // Mis Add 2007/07/23
    211                                                        renderer.drawItem(g2, state, dataArea, this,
    212                                                                        domainAxis, rangeAxis, currentDataset,
    213                                                                        row, column, pass);
    214                                                }
    215                                                // 指定のシリーズと一致する場合は、最後に描画する。 Mis Add 2007/07/23
    216                                                if( serNo >= 0 ) {
    217                                                        renderer.drawItem(g2, state, dataArea, this,
    218                                                                        domainAxis, rangeAxis, currentDataset,
    219                                                                        serNo, column, pass);
    220                                                }
    221                                        }
    222                                }
    223                        }
    224                }
    225                return foundData;
    226        }
    227
    228        /**
    229         * CategoryPlot の drawRangeGridlines メソッドをオーバーライドします。
    230         *
    231         * Draws the gridlines for the plot.
    232         *
    233         * @og.rev 6.0.2.0 (2014/09/19) rangeSkip 属性 の初期値を、-1(設定なし)に変更
    234         *
    235         * @param g2  the graphics device.
    236         * @param dataArea      the area inside the axes.
    237         * @param ticks  the ticks.
    238         * @see #drawDomainGridlines(Graphics2D, Rectangle2D)
    239         */
    240        @SuppressWarnings("rawtypes")
    241        @Override
    242        protected void drawRangeGridlines( final Graphics2D g2, final Rectangle2D dataArea,
    243                                                                           final List ticks ) {
    244                // draw the range grid lines, if any...
    245                // 6.0.2.0 (2014/09/19) rangeSkip 属性 の初期値を、-1(設定なし)に変更
    246                if( rangeSkip <= 0 ) { super.drawRangeGridlines( g2,dataArea,ticks ); }
    247                else {
    248                        if( isRangeGridlinesVisible() ) {
    249                                final Stroke gridStroke = getRangeGridlineStroke();
    250                                final Paint gridPaint = getRangeGridlinePaint();
    251                                if( gridStroke != null && gridPaint != null ) {
    252                                        final ValueAxis axis = getRangeAxis();
    253                                        final CategoryItemRenderer renderer1 = getRenderer();
    254                                        if( axis != null && renderer1 != null ) {
    255                                                final Iterator<?> iterator = ticks.iterator();
    256                                                int cnt = 0;
    257                                                while( iterator.hasNext() ) {
    258                                                        final ValueTick tick = (ValueTick) iterator.next();
    259                                                        if( cnt % rangeSkip == 0 ) {
    260                                                                renderer1.drawRangeGridline(g2, this,
    261                                                                                        axis, dataArea, tick.getValue());
    262                                                        }
    263                                                        cnt++ ;
    264                                                }
    265                                        }
    266                                }
    267                        }
    268                }
    269        }
    270
    271        /**
    272         * この文字列と指定されたオブジェクトを比較します。
    273         *
    274         * 親クラスで、equals メソッドが実装されているため、警告がでます。
    275         *
    276         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    277         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    278         *
    279         * @param       object  比較するオブジェクト
    280         *
    281         * @return      Objectが等しい場合は true、そうでない場合は false
    282         */
    283        @Override
    284        public boolean equals( final Object object ) {
    285                // 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
    286                return super.equals( object ) && hsCode == ((HybsCategoryPlot)object).hsCode;
    287        }
    288
    289        /**
    290         * このオブジェクトのハッシュコードを取得します。
    291         *
    292         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    293         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    294         *
    295         * @return      ハッシュコード
    296         */
    297        @Override
    298        public int hashCode() { return hsCode ; }
    299}