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         * 少ない数のグリッド線(例えば、2つおき)を出す場合に、値を設定します。
115         * "1" (初期値)では、1つづつ表示(つまり、すべて表示する)します。
116         * "2" とすると、1つおきに、"3" とすると、2つおきに表示します。
117         * なお、先頭から表示を開始します。
118         * (独自メソッド)
119         *
120         * 6.0.2.0 (2014/09/19) 前回の JFreeChart のVerUpで、グリッド線の表示が
121         * 5個おきに変わったようです。設定する値を マイナスにすると、初期設定の値を
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}