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