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.plugin.view; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.fukurou.util.StringUtil; 021 022import java.util.Calendar; 023import java.util.Map; 024import java.util.TreeMap; 025import java.util.Collection; 026import java.util.Locale ; 027import java.util.Date; 028import java.text.DateFormat; 029import java.text.SimpleDateFormat; 030 031import java.awt.Graphics2D; 032import java.awt.Color; 033import java.awt.FontMetrics; 034import java.awt.Stroke; 035import java.awt.BasicStroke; 036import java.awt.image.BufferedImage; 037 038import javax.imageio.ImageIO; 039import java.io.File; 040import java.io.IOException; 041 042 043/** 044 * キー、日時、状況コードを持つ稼働状況の表示を行うクラスです。 045 * 046 * パラメータが必要な場合は、ViewTimeBarParamTag を使用してください。 047 * 048 * パラメータが設定されていない場合は、ViewForm_ImageTimeBar の初期値が使用されます。 049 * (パラメータを使用するには、viewタグのuseParam 属性をtrueに設定する必要があります。) 050 * 051 * SELECT文は、キー、日時、状況コードが、必須項目で、カラムの並び順は、完全に固定です。 052 * よって、カラム位置を指定する必要はありませんが、SELECT文を自由に設定することも 053 * 出来ませんので、ご注意ください。 054 * この固定化に伴い、WRITABLE 指定も使用できません。(そもそも書き込み不可です) 055 * それ以降のカラムについては、内部処理としては、使用していません。 056 * ただし、パラメータで、カラー色指定、ラベル表記部、イメージ重ね合わせ、 057 * ポップアップ表記、リンク表記に使えます。 058 * 059 * データの並び順(ORDER BY)も、キー、日時順にしてください。 060 * データは、キー単位に1レコード作成されます。(キーブレイク)その間、日時順に 061 * データを処理します。 062 * 063 * データの表示は、今のレコードの日時から、次のレコードの日時までを一つの状態と 064 * して表します。今のレコードを表示するには、次のレコードが必要になります。 065 * 画面表示は、表示開始日時(minStartTime) から 表示期間(timeSpan)分を表示します。 066 * 通常、開始時刻は、表示開始時刻より前より始まり、次のレコードで、終了時刻が決定 067 * されます。最後のデータは、期間満了まで続いていると仮定されます。 068 * データが存在しないのであれば、「存在しないデータ」を作成してください。 069 * 070 * ImageTimeBar では、キーでまとめた値について、各状況コードをカラー化し、積み上げ 071 * 帯グラフ形式でPNG画像化します。 072 * この画像を、読み込む HTML を出力することで、画面上に、積み上げ帯グラフを表示します。 073 * 状況コードに対応する色は、標準では自動作成ですが、外部から色文字列(colorClm)を与えることで 074 * 自由に指定する事も可能です。 075 * 076 * ポップアップ表記(tipsClm)、リンク表記(linkClm)は、この画像に対するエリア指定タグを出力する事で実現します。 077 * 画像ファイルは、全データに対して、1画像だけなので、サイズは大きくなりますが、1レコード 078 * 単位に画像を作成しないため、レスポンスは向上します。 079 * それぞれ、viewMarker , viewLink を利用することが可能です。特に、リンク表記(linkClm) については、 080 * linkタグの hrefTarget 属性を true に設定することで適用できます。 081 * 082 * 画像ファイルは、java.io.File.createTempFile( File ) で作成するため、JavaVM(=Tomcat)が 083 * 正常終了するときに、削除されます。異常終了時には残りますが、temp フォルダを定期的に 084 * 整理すれば、それほど大量のファイルが残ることはないと思われます。 085 * 086 * データは、イベント発生時に作成されると仮定しています。つまり、書き込まれた日時から、 087 * 状況コードに対応する状況が発生し、次の状況違いのレコードまで継続していると考えます。 088 * よって、データを途中で切り出す場合、切り出す範囲の前の状態が必要になります。 089 * 一番最初の状態は、"不明" として扱います。(空欄=白色) 090 * 091 * @og.rev 5.5.5.6 (2012/08/31) 新規追加 092 * @og.group 画面表示 093 * 094 * @version 4.0 095 * @author Kazuhiko Hasegawa 096 * @since JDK5.0, 097 */ 098public class ViewForm_ImageTimeBar extends ViewForm_HTMLTable { 099 //* このプログラムのVERSION文字列を設定します。 {@value} */ 100 private static final String VERSION = "5.6.5.0 (2013/06/07)" ; 101 102 private static final Color LABEL_COLOR = Color.BLACK; // ラベル記述時の色 103 private static final Color NULL_COLOR = Color.WHITE; // 5.6.1.1 (2013/02/08) 不明(空欄)時の色 104 105 private long startDate ; // タイムテーブルの表示開始日時から求めた long 値(1=分単位) 106 private long timeSpan ; // タイムテーブルの表示期間。元は時間指定であるが、分単位で持つ。(1=分単位) 107 108 private boolean useLegend ; // カラーの凡例を使用するかどうか[true/false] 109 private int maxLabelWidth ; // ラベル表記部の最大サイズをpxで指定。何もなければ、可変長サイズ 110 private int maxTimeWidth ; // タイム表記部の最大サイズをpxで指定。 111 private int chartHeight ; // 1レコードのチャートの間隔をpxで指定。実際の幅は、CHART_HEIGHT+MARGIN*2 112 private int chartPadding ; // イメージ作成の 全体テーブルの隙間 113 private int recodeMargin ; // 各レコード、文字等の内部の間隔 114 private boolean useLastData ; // 5.6.1.1 (2013/02/08) 行の最後の情報が、継続しているとして使うかどうか[true/false] 115 116 private String tempDir ; // 画像ファイルを作成するテンポラリディレクトリ(相対パス) 117 private String tempUrl ; // 作成した画像ファイルを取得するときに使用するURL(コンテキスト/相対パス) 118 119 // SELECT文は、キー、日時、状況コードが、必須項目 120 private static final int keyClmNo = 0; // ヘッダー1:キーカラムNo 121 private static final int dyClmNo = 1; // ヘッダー2:日時カラムNo 122 private static final int fgjClmNo = 2; // ヘッダー3:状況コードカラムNo 123 124 private int[] labelClmsNo = null; // 初期値は、キーカラム 125 private int[] maxClmWidth = null; // labelClms 単位の最大文字列長 126 private int colClmNo = -1; // カラーコード直接指定する場合に色文字列を指定するカラムNo 127 private int tipsClmNo = -1; // マウスオーバー時のTips表示を行うカラムNo 128 private int linkClmNo = -1; // クリッカブルリンクを設定するカラムNo 129 130 private int str2DateTime ; // getStr2Date(String)処理をおこなった時の、引数の時刻情報(分単位)をセットするテンポラリ変数。 131 private int startTime ; // startDate の時刻情報。上記処理で、startDate を getStr2Date(String) 処理したときの値を持っておくための変数。 132 133 private int MAX_X ; // イメージの最大横幅(X)方向のサイズpx。chartPadding*2 + maxLabelWidth + maxTimeWidth 134 private int MAX_Y ; // イメージの最大縦幅(Y)方向のサイズpx。chartPadding*2 + (chartHeight+recodeMargin*2)*(レコード数+ヘッダー数) 135 136 /** 137 * 内容をクリア(初期化)します。 138 * 139 */ 140// @Override 141// public void clear() { 142// super.clear(); 143// } 144 145 /** 146 * DBTableModel から HTML文字列を作成して返します。 147 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。 148 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。 149 * 150 * @og.rev 5.6.1.0 (2013/02/01) tips や link は、ひとつ前のデータで作成する必要がる為、最初の1件目は、処理しないように修正 151 * @og.rev 5.6.1.1 (2013/02/08) 初期値の色を、直接の値ではなく、static final で定義された色を使用する。色自体は変えていません 152 * @og.rev 5.6.1.1 (2013/02/08) useLastData 追加 153 * @og.rev 5.6.3.0 (2013/04/01) tipsのシングルクォーテーション のエスケープ 154 * @og.rev 5.6.3.1 (2013/04/05) 短縮ラベルなど、<span>タグが付与される値から、spanタグを削除します。 155 * 156 * @param startNo 表示開始位置 157 * @param pageSize 表示件数 158 * 159 * @return DBTableModelから作成された HTML文字列 160 */ 161 @Override 162 public String create( final int startNo, final int pageSize ) { 163 if( getRowCount() == 0 ) { return ""; } // 暫定処置 164 165 // パラメータの情報を初期化&取得します。 166 paramInit(); 167 168 int lastNo = getLastNo( startNo, pageSize ); 169 170 // イメージの 最大X、Yサイズを求め、結果を、MAX_X,MAX_Y 変数に設定する。 171 calcImageSize( startNo,lastNo ); 172 173 BufferedImage img = new BufferedImage( MAX_X, MAX_Y, BufferedImage.TYPE_INT_ARGB); 174 Graphics2D g2 = img.createGraphics(); 175 176 // chartPadding を考慮した領域のクリップ(クリップ外にDrowされても無視されます。) 177 g2.setClip( chartPadding , chartPadding , MAX_X-chartPadding*2+1 , MAX_Y-chartPadding*2+1 ); // (x 座標,y 座標,幅,高さ) +1は罫線の分 178 179 StringBuilder out = new StringBuilder( HybsSystem.BUFFER_LARGE ); 180 181 String oldKeyVal = ""; // 初期値。1回目にキーブレイクさせる。 182 long oldTime = 0L; // 日付項目の一つ前の値 183 184// Color oldColor = Color.WHITE; // 色の一つ前の値(初期値) 185 Color oldColor = NULL_COLOR; // 5.6.1.1 (2013/02/08) 不明(空欄)時の色(初期値) 186 187 ColorMap colMap = new ColorMap(); // 状況コード、ラベル、カラーを管理するクラス 188 189 int rowCnt = (useLegend) ? 2 : 1; // 凡例(useLegend)を使う(true)場合は、2行分、使わない場合は、ヘッダーの1行が初期値 190 int imgX = 0; // イメージ出力時の X軸の左端 px数 191 int imgY = 0; // イメージ出力時の Y軸の上端 px数 192 int imgW = 0; // イメージ出力時の 幅(fillRectで使用) 193 int imgH = chartHeight; // イメージ出力時の 高さ(fillRectで使用) 194 int rowH = chartHeight+recodeMargin*2; // 罫線出力時の高さ(drawRectで使用) 195 196 double timeScale = (double)maxTimeWidth/(double)timeSpan; 197 boolean useTipsLink = (tipsClmNo >= 0 || linkClmNo >= 0) ; // tipsClm か linkClm かどちらかを使用する場合、true 198 for( int row=startNo; row<lastNo; row++ ) { 199 // キーブレイクの判定 200 String keyVal = getValue( row,keyClmNo ); 201 String dyVal = getValue( row,dyClmNo ); 202 203 // キーブレイク判定。キーブレイクは、一番初めから来る。 204 if( !oldKeyVal.equals( keyVal ) ) { 205 // キーブレイクで最初に行うのは、前のレコードの未出力分の処理。1行目は、処理不要 206 if( row > startNo ) { 207 imgX = chartPadding + maxLabelWidth + (int)(oldTime*timeScale); 208 imgW = (int)((timeSpan-oldTime)*timeScale) ; 209 210 // 幅が0以上の場合のみ描画する。 211 if( imgW > 0 ) { 212 // 5.6.1.1 (2013/02/08) useLastData 追加 213 if( useLastData ) { g2.setColor( oldColor ); } // 色の設定は、旧色を使う 214 else { g2.setColor( NULL_COLOR ); } // NULL色を使う 215 g2.fillRect( imgX , imgY+recodeMargin , imgW, imgH ); // (実際の状態)左端x,上端y,幅w,高さh 216 217 // tipsClm か linkClm を使用する。 218 // 5.6.1.0 (2013/02/01) tips や link は、ひとつ前のデータで作成する必要がる為、最初の1件目は、処理しないように修正 219// if( useTipsLink ) { 220// if( useTipsLink && row > 0 ) { 221 if( useLastData && useTipsLink && row > 0 ) { // 5.6.1.1 (2013/02/08) useLastData 追加 222 // tips や link は、ひとつ前のデータで作成する必要がある。(row-1) 223 String tips = (tipsClmNo >= 0) ? getValueLabel( row-1,tipsClmNo ) : getValueLabel( row-1,fgjClmNo ) ; 224 if( tips != null && tips.indexOf( "'" ) >= 0 ) { // 5.6.3.0 (2013/04/01) シングルクォーテーション のエスケープ 225 tips = tips.replaceAll( "'","'" ); 226 } 227 tips = StringUtil.spanCut( tips ); // 5.6.3.1 (2013/04/05) spanタグを削除 228 229 out.append( "<area shape='rect' alt='" ).append( tips ).append( "'" ); 230 if( linkClmNo >= 0 ) { 231 String hrefVal = getValueLabel( row-1,linkClmNo ); 232 if( hrefVal != null && hrefVal.startsWith( "href" ) ) { // linkClmの値の先頭が、html の場合のみ追加する。 233 out.append( hrefVal ); 234 } 235 } 236 out.append( " coords='" ).append( imgX ).append( "," ).append( imgY+recodeMargin ).append( "," ); // 左端x1,上端y1 237 out.append( imgX+imgW ).append( "," ).append( imgY+recodeMargin+chartHeight ).append( "' />" ); // 右端x2,下段y2 238 } 239 } 240 } 241 242 // 次に、新しい行の出力(ラベルは、ブレイク時に出力しておきます。) 243 oldKeyVal = keyVal; // null は来ないはず 244 imgY = chartPadding+(rowH)*rowCnt ; 245 rowCnt++ ; 246 247 // レコードのラベル分だけ、繰返し描画する。 248 int clmSu = labelClmsNo.length; 249 int posX = chartPadding ; // ラベル文字列の書き出し位置の初期値 250 int posY2 = imgY+chartHeight+recodeMargin ; // ラベルのY軸基準。 251 g2.setColor( LABEL_COLOR ); 252 for( int col=0; col<clmSu; col++ ) { 253 String lbl = getValueLabel( row,labelClmsNo[col] ); // その行の値のRenderer値 254 lbl = StringUtil.spanCut( lbl ); // 5.6.3.1 (2013/04/05) spanタグを削除 255 g2.drawString( lbl , posX + recodeMargin, posY2 ); // 文字列,ベースラインのx座標,y座標 256 posX += recodeMargin*2 + maxClmWidth[col] ; // 次の書き出し位置は、文字最大長+マージン 257 } 258 259 // レコードの枠線 260 g2.drawRect( chartPadding , imgY , MAX_X-chartPadding*2 , rowH ); // (レコード枠)左端x,上端y,幅w,高さh 261 262 oldTime = 0L; // キーブレイクによる初期化 263// oldColor= Color.WHITE; // キーブレイクによる初期化 264 oldColor= NULL_COLOR; // 5.6.1.1 (2013/02/08) キーブレイクによる初期化=不明(空欄)時の色 265 } 266 267 long newTime = getStr2Date( dyVal ) - startDate; // 次の時刻 268 if( newTime < oldTime ) { newTime = oldTime; } // 前の時刻より小さい場合は、前の時刻まで、進めておく。 269 270 imgX = chartPadding + maxLabelWidth + (int)(oldTime*timeScale); // old時刻から書き始める(左端x) 271 imgW = (int)((newTime-oldTime)*timeScale) ; // 差分幅だけ描画する。 272 273 // 幅が0以上の場合のみ描画する。 274 if( imgW > 0 ) { 275 g2.setColor( oldColor ); // 色の設定 276 g2.fillRect( imgX , imgY+recodeMargin , imgW, chartHeight ); // (実際の状態)左端x,上端y,幅w,高さh 277 278 // tipsClm か linkClm を使用する。 279 // 5.6.1.0 (2013/02/01) tips や link は、ひとつ前のデータで作成する必要がる為、最初の1件目は、処理しないように修正 280// if( useTipsLink ) { 281 if( useTipsLink && row > 0 ) { 282 // tips や link は、ひとつ前のデータで作成する必要がある。(row-1) 283 String tips = (tipsClmNo >= 0) ? getValueLabel( row-1,tipsClmNo ) : getValueLabel( row-1,fgjClmNo ) ; 284 if( tips != null && tips.indexOf( "'" ) >= 0 ) { // 5.6.3.0 (2013/04/01) シングルクォーテーション のエスケープ 285 tips = tips.replaceAll( "'","'" ); 286 } 287 tips = StringUtil.spanCut( tips ); // 5.6.3.1 (2013/04/05) spanタグを削除 288 289 out.append( "<area shape='rect' alt='" ).append( tips ).append( "'" ); 290 if( linkClmNo >= 0 ) { 291 String hrefVal = getValueLabel( row-1,linkClmNo ); 292 if( hrefVal != null && hrefVal.startsWith( "href" ) ) { // linkClmの値の先頭が、html の場合のみ追加する。 293 out.append( hrefVal ); 294 } 295 } 296 out.append( " coords='" ).append( imgX ).append( "," ).append( imgY+recodeMargin ).append( "," ); // 左端x1,上端y1 297 out.append( imgX+imgW ).append( "," ).append( imgY+recodeMargin+chartHeight ).append( "' />" ); // 右端x2,下段y2 298 } 299 } 300 301 oldTime = newTime ; 302 303 String fgjVal = getValue( row,fgjClmNo ); // 状況コードのキー 304 String fgjLbl = getValueLabel( row,fgjClmNo ); // 状況コードのラベル 305 if( colClmNo >= 0 ) { 306 oldColor = colMap.getColor( fgjVal,fgjLbl,getValue( row,colClmNo ) ); // 色文字列を指定する場合 307 } 308 else { 309 oldColor = colMap.getColor( fgjVal,fgjLbl ); // 色文字列を指定しない=自動設定 310 } 311 } 312 313 // レコードのいちばん最後の出力。一応、ジャストの場合(oldTime == maxEdTime)は、処理しない 314 // 5.6.1.1 (2013/02/08) レコードのいちばん最後の出力は、NULL色を使うように変更 315 if( oldTime < timeSpan ) { 316 imgX = chartPadding + maxLabelWidth + (int)(oldTime*timeScale); 317 imgW = (int)((timeSpan-oldTime)*timeScale) ; 318 // 5.6.1.1 (2013/02/08) useLastData 追加 319 if( useLastData ) { g2.setColor( oldColor ); } // 色の設定は、旧色を使う 320 else { g2.setColor( NULL_COLOR ); } // NULL色を使う 321 g2.fillRect( imgX , imgY+recodeMargin , imgW, chartHeight ); // (実際の状態)左端,上端,幅,高さ 322 323 // tipsClm か linkClm を使用する。 324// if( useTipsLink ) { 325 if( useLastData && useTipsLink ) { // 5.6.1.1 (2013/02/08) useLastData 追加 326 // tips や link 最後の出力は、最後のデータなので、lastNo-1 となる。 327 String tips = (tipsClmNo >= 0) ? getValueLabel( lastNo-1,tipsClmNo ) : getValueLabel( lastNo-1,fgjClmNo ) ; 328 if( tips != null && tips.indexOf( "'" ) >= 0 ) { // 5.6.3.0 (2013/04/01) シングルクォーテーション のエスケープ 329 tips = tips.replaceAll( "'","'" ); 330 } 331 tips = StringUtil.spanCut( tips ); // 5.6.3.1 (2013/04/05) spanタグを削除 332 333 out.append( "<area shape='rect' alt='" ).append( tips ).append( "'" ); 334 if( linkClmNo >= 0 ) { 335 String hrefVal = getValueLabel( lastNo-1,linkClmNo ); 336 if( hrefVal != null && hrefVal.startsWith( "href" ) ) { // linkClmの値の先頭が、html の場合のみ追加する。 337 out.append( hrefVal ); 338 } 339 } 340 out.append( " coords='" ).append( imgX ).append( "," ).append( imgY+recodeMargin ).append( "," ); // 左端x1,上端y1 341 out.append( imgX+imgW ).append( "," ).append( imgY+recodeMargin+chartHeight ).append( "' />" ); // 右端x2,下段y2 342 } 343 } 344 345 // ヘッダー情報のイメージを作成します。 346 imageHeaderPaint( g2 , timeScale , colMap ); 347 348 // イメージファイルを出力し、URLを返します。 349 File file = null; 350 try { 351 File dir = new File( tempDir ); // 画像ファイル出力先のフォルダ 352 dir.mkdirs(); // なければ作成する。 353 354 file = File.createTempFile( "Img",".png", dir ); // テンポラリファイルの作成 355 file.deleteOnExit(); // JavaVM 停止時にテンポラリファイルの削除 356 357 ImageIO.write(img, "PNG", file ); 358 g2.dispose(); 359 } 360 catch( IOException ex ) { 361 System.out.println( "エラー:" + ex ); 362 } 363 364 // imgタグを作成します。 365 int width = MAX_X; 366 int height = MAX_Y; 367 368 StringBuilder out2 = new StringBuilder( HybsSystem.BUFFER_LARGE ); 369 out2.append( "<img id='ImageTimeBar' alt='ImageTimeBar' border='0'" ); 370 out2.append( " width='" ).append( width ).append( "px' height='" ).append( height ).append( "px'" ); 371 out2.append( " src='" ).append( tempUrl ).append( file.getName() ).append( "'" ); 372 373 // area タグのデータが作成されていれば、出力します。 374 if( out.length() > 0 ) { 375 out2.append( " usemap='#TimeBarMap' />" ); // img タグにusemap 属性を追加して、終了部分を追記 376 377 out2.append( "<map name='TimeBarMap'>" ); 378 out2.append( out ); 379 out2.append( "</map>" ); 380 } 381 else { 382 out2.append( " />" ); // img タグの終了部分を追記 383 } 384 385 return out2.toString(); 386 } 387 388 /** 389 * パラメータ内容を初期化します。 390 * 391 * @og.rev 5.6.1.1 (2013/02/08) useLastData 追加 392 */ 393 private void paramInit() { 394 String s_startDate = getParam( "START_DATE" ); // タイムテーブルの表示開始日時をセットします(初期値:データの最小日時)。 395 int timeSpanHour = getIntParam( "TIME_SPAN" ); // タイムテーブルの表示期間を時間で指定します(初期値:{@og.value TIME_SPAN})。 396 397 String[] s_labelClms = StringUtil.csv2Array( getParam( "LABEL_CLMS" ) ); // 一覧表のラベル表示部に表示するカラム名をCSV形式で指定します。 398 String s_colClm = getParam( "COLOR_CLM" ); // レコードに付ける色を色文字列で指定する場合のカラム名を指定します。 399 String s_tipsClm = getParam( "TIPS_CLM" ); // レコード単位に、マウスオーバー時のTips表示を行うカラム名を指定します。 400 String s_linkClm = getParam( "LINK_CLM" ); // レコード単位に、クリッカブルリンクを設定するカラム名を指定します。 401 402 useLegend = getBoolParam( "USE_LEGEND" ); // カラーの凡例を使用するかどうか[true/false] 403 maxLabelWidth = getIntParam( "MAX_LABEL_WIDTH" ); // ラベル表記部の最大サイズをpxで指定。何もなければ、可変長サイズ 404 maxTimeWidth = getIntParam( "MAX_TIME_WIDTH" ); // タイム表記部の最大サイズをpxで指定。 405 chartHeight = getIntParam( "CHART_HEIGHT" ); // 1レコードのチャートの間隔をpxで指定。実際の幅は、CHART_HEIGHT+MARGIN*2 406 chartPadding = getIntParam( "CHART_PADDING" ); // イメージ作成の 全体テーブルの隙間 407 recodeMargin = getIntParam( "RECODE_MARGIN" ); // 各レコード、文字等の内部の間隔 408 useLastData = getBoolParam( "USE_LAST_DATA" ); // 5.6.1.1 (2013/02/08) 行の最後の情報が、継続しているとして使うかどうか[true/false] 409 410 tempDir = getParam( "TEMP_DIR" ); // 画像ファイルを作成するテンポラリディレクトリ(相対パス) 411 tempUrl = getParam( "TEMP_URL" ); // 作成した画像ファイルを取得するときに使用するURL(コンテキスト/相対パス) 412 413 startDate = getStr2Date( s_startDate ); // 分単位に変換する。 414 startTime = str2DateTime ; // 開始日時の時刻情報(分単位)の値。str2DateTime は、getStr2Date(String)メソッド実行後にセットされる。 415 timeSpan = timeSpanHour * 60L ; // 分単位に変換する。 416 417 int len = s_labelClms.length; 418 if( len > 0 ) { 419 labelClmsNo = new int[len]; 420 for( int i=0; i<len; i++ ) { 421 labelClmsNo[i] = getColumnNo( s_labelClms[i] ); 422 } 423 } 424 else { 425 labelClmsNo = new int[] { keyClmNo }; // 初期値は、キーカラム 426 } 427 428 // 指定のカラム名に対するカラム番号を取得。なければ、-1 を設定しておく。 429 if( s_colClm != null ) { colClmNo = getColumnNo( s_colClm ); } // レコードに付ける色を色文字列で指定する場合のカラムNo 430 if( s_tipsClm != null ) { tipsClmNo = getColumnNo( s_tipsClm ); } // レコード単位に、マウスオーバー時のTips表示を行うカラムNo 431 if( s_linkClm != null ) { linkClmNo = getColumnNo( s_linkClm ); } // レコード単位に、クリッカブルリンクを設定するカラムNo 432 } 433 434 /** 435 * イメージの XYサイズを求め、結果を、MAX_X,MAX_Y 変数に設定します。 436 * 437 * また、ラベル文字列の最大長が指定されていない場合(maxLabelWidth == -1)最大長を自動計算し、maxLabelWidth変数にセットします。 438 * 439 * maxLabelWidth : -1 の場合は、ラベル文字列の最大長を求めて、この値を計算する。= (recodeMargin*2 + ラベル文字列の最大長) 440 * MAX_X : イメージの最大横幅(X)方向のサイズpx。chartPadding*2 + maxLabelWidth + maxTimeWidth 441 * MAX_Y : イメージの最大縦幅(Y)方向のサイズpx。chartPadding*2 + (chartHeight+recodeMargin*2)*(レコード数+ヘッダー数) 442 * 443 * @og.rev 5.6.3.1 (2013/04/05) 短縮ラベルなど、<span>タグが付与される値から、spanタグを削除します。 444 * 445 * @param startNo 計算の開始列番号 446 * @param lastNo 計算の終了列番号 447 */ 448 private void calcImageSize( final int startNo , final int lastNo ) { 449 String oldKeyVal = ""; // 初期値。1回目にキーブレイクさせる。 450 451 int clmSu = labelClmsNo.length; 452 maxClmWidth = new int[clmSu]; 453 454 int rowCnt = (useLegend) ? 2 : 1; // 凡例(useLegend)を使う(true)場合は、2行分、使わない場合は、ヘッダーの1行が初期値 455 456 // ラベル領域の長さを各ラベル長を積み上げて計算する。 457 if( maxLabelWidth < 0 ) { 458 // FontMetrics を取得する為だけの BufferedImage 459 BufferedImage img = new BufferedImage( 1, 1, BufferedImage.TYPE_INT_ARGB); 460 Graphics2D g2 = img.createGraphics(); 461 FontMetrics fontM = g2.getFontMetrics(); 462 463 // 初期値の計算は、ヘッダーのラベルの幅を使用する。 464 for( int col=0; col<clmSu; col++ ) { 465 String lbl = getColumnLabel( labelClmsNo[col] ); // ヘッダーのラベル 466 lbl = StringUtil.spanCut( lbl ); // 5.6.3.1 (2013/04/05) spanタグを削除 467 maxClmWidth[col] = fontM.stringWidth( lbl ); // 最大値の初期値として、ヘッダーラベルの幅をセットしておく。 468 } 469 470 for( int row=startNo; row<lastNo; row++ ) { 471 // キーブレイク判定。キーブレイクは、一番初めから来る。 472 String keyVal = getValue( row,keyClmNo ); 473 if( !oldKeyVal.equals( keyVal ) ) { 474 oldKeyVal = keyVal; 475 rowCnt++; // レコード数 476 477 // ラベルは、キーブレイク時の値のみ採用する。 478 for( int col=0; col<clmSu; col++ ) { 479 String lbl = getValueLabel( row,labelClmsNo[col] ); // その行の値のRenderer値 480 lbl = StringUtil.spanCut( lbl ); // 5.6.3.1 (2013/04/05) spanタグを削除 481 int fontW = fontM.stringWidth( lbl ); 482 if( maxClmWidth[col] < fontW ) { maxClmWidth[col] = fontW; } 483 } 484 } 485 } 486 g2.dispose(); 487 488 // 最大ラベル幅は、各ラベルの最大値+(マージン*2)*カラム数 489 maxLabelWidth = recodeMargin * 2 * clmSu ; 490 for( int col=0; col<clmSu; col++ ) { 491 maxLabelWidth += maxClmWidth[col]; 492 } 493 } 494 else { 495 for( int row=startNo; row<lastNo; row++ ) { 496 // キーブレイク判定。キーブレイクは、一番初めから来る。 497 String keyVal = getValue( row,keyClmNo ); 498 if( !oldKeyVal.equals( keyVal ) ) { 499 oldKeyVal = keyVal; 500 rowCnt++; // レコード数 501 } 502 } 503 504 // 最大ラベル幅は、均等割り付け。端数は無視(どうせ、ラベル部は、maxLabelWidth で計算するので。) 505 int clmWidth = ( maxLabelWidth - recodeMargin * 2 * clmSu ) / clmSu ; 506 for( int col=0; col<clmSu; col++ ) { 507 maxClmWidth[col] = clmWidth; 508 } 509 } 510 511 MAX_X = chartPadding*2 + maxLabelWidth + maxTimeWidth ; 512 MAX_Y = chartPadding*2 + (chartHeight+recodeMargin*2)*rowCnt ; 513 } 514 515 /** 516 * ヘッダー情報のイメージを作成します。 517 * 518 * 全体の枠もここで作成しておきます。 519 * イメージは、キーカラムのラベルと、時間軸になります。時間軸は縦方向にすべて線を引きます。 520 * 時間軸の間隔は、timeScale によって、切り替わります。 521 * 凡例を使う場合(useLegend=true)は、引数の ColorMap を利用して作成します。 522 * 523 * @og.rev 5.6.3.1 (2013/04/05) 短縮ラベルなど、<span>タグが付与される値から、spanタグを削除します。 524 * @og.rev 5.6.5.0 (2013/06/07) 年月日情報を表示します。なお、日単位の場合は、年月は省略します。 525 * 526 * @param g2 描画するGraphics2Dオブジェクト 527 * @param timeScale 時間(分)当たりのピクセル数 528 * @param colMap 状況コードに対応したカラーマップ 529 */ 530 private void imageHeaderPaint( final Graphics2D g2 , final double timeScale , final ColorMap colMap ) { 531 532 int posY1 = chartPadding ; 533 int posY2 = chartPadding+chartHeight+recodeMargin ; 534 535 // 凡例を使う場合 536 if( useLegend && colMap != null ) { 537 // 凡例を並べる間隔を求める。 538 FontMetrics fontM = g2.getFontMetrics(); 539 int maxSize = fontM.stringWidth( colMap.getMaxLengthLabel() ) ; // 文字列の最大長ラベルの幅 540 int imgW = chartHeight ; // 凡例■の幅(高さと同じにしているので真四角) 541 int mgnW = recodeMargin ; // 凡例■から文字までの間 542 int spanW = maxSize + recodeMargin ; // 凡例■から凡例■までの間隔。文字最大長+α 543 544 int legX = chartPadding ; 545 for( Object[] obj : colMap.values() ) { 546 String lbl = (String)obj[0]; 547 Color col = (Color)obj[1]; 548 549 g2.setColor( col ); // 凡例■の色 550 g2.fillRect( legX , posY1+recodeMargin , imgW , chartHeight ); // (実際の状態)左端x,上端y,幅w,高さh 551 552 legX += imgW + mgnW ; 553 g2.setColor( LABEL_COLOR ); 554 g2.drawString( lbl , legX , posY2 ); // 文字列,ベースラインのx座標,y座標 555 556 legX += spanW ; 557 } 558 posY1 += chartHeight+recodeMargin*2 ; // 1レコード分の高さを加算しておく。 559 posY2 += chartHeight+recodeMargin*2 ; // 1レコード分の高さを加算しておく。 560 } 561 562 // まずは、全体の枠線の描画 563 g2.setColor( LABEL_COLOR ); 564 g2.drawRect( chartPadding, posY1, MAX_X-chartPadding*2, MAX_Y-posY1-chartPadding ); // 左端,上端,幅,高さ 565 566 // ヘッダーのラベル分だけ、繰返し描画する。 567 int clmSu = labelClmsNo.length; 568 int posX = chartPadding ; // ラベル文字列の書き出し位置の初期値 569 for( int col=0; col<clmSu; col++ ) { 570 String lbl = getColumnLabel( labelClmsNo[col] ); // ヘッダーのラベル 571 lbl = StringUtil.spanCut( lbl ); // 5.6.3.1 (2013/04/05) spanタグを削除 572 g2.drawString( lbl , posX + recodeMargin, posY2 ); // 文字列,ベースラインのx座標,y座標 573 posX += recodeMargin*2 + maxClmWidth[col] ; // 次の書き出し位置は、文字最大長+マージン*2 574 g2.drawLine( posX, posY1, posX, MAX_Y-chartPadding ); // 始点(x 座標,y 座標),終点(x 座標,y 座標) 575 } 576 577 int step = TimeScaleStep.getStep( timeScale ); // 時間スケールに対応したSTEP数 578 579 // 日付ヘッダー部の描画のためのカレンダー 580 Calendar cal = Calendar.getInstance(); 581 cal.setTimeInMillis( startDate * 60000L ); 582 583 int week = cal.get( Calendar.DAY_OF_WEEK ); // 開始曜日 584 585 String fmt = ( step < 1440 ) ? "yyyy/MM/dd(EE)" : "dd" ; // 上段フォーマットは、時間ベースの場合は、"yyyy/MM/dd(EE)" 、日ベースの場合は、"dd" 586 DateFormat format1 = new SimpleDateFormat( fmt,Locale.JAPAN ); 587 588 // グラフ領域の日付ヘッダー部の描画 589 g2.setStroke( DSAH_STROK ); // 日付部は、破線 590 posX = chartPadding+maxLabelWidth ; // グラフ領域は、chartPadding+maxLabelWidth から。 591 for( int tm = 0; tm < timeSpan; tm+=step ) { 592 593 int offset = chartHeight / 2 + recodeMargin; // ヘッダーの表示基準のオフセット(チャート幅の半分) 594 595 // 上段:ヘッダー出力 596 if( ( tm % 1440 ) == 0 ) { 597 Date dt = new Date( (startDate + tm) * 60000L ); 598 g2.drawString( format1.format( dt ) , posX + recodeMargin , posY2-offset ); // 文字列,ベースラインのx座標,y座標 599 offset = 0; // ヘッダーを表示する場合のみ上まで線を引く。 600 } 601 602 g2.drawString( getTime2Str(startTime+tm,step,week) , posX + recodeMargin , posY2 ); // 文字列,ベースラインのx座標,y座標 603 g2.drawLine( posX, posY1+offset, posX, MAX_Y-chartPadding ); // 始点(x 座標,y 座標),終点(x 座標,y 座標) 604 605// g2.drawString( getTime2Str(startTime+tm,step) , posX + recodeMargin , posY2 ); // 文字列,ベースラインのx座標,y座標 606// g2.drawLine( posX, posY1-10, posX, MAX_Y-chartPadding-10 ); // 始点(x 座標,y 座標),終点(x 座標,y 座標) 607 posX += (int)(step*timeScale); 608 } 609 } 610 611 // imageHeaderPaintメソッドで使用する 破線の定義 612 private static final Stroke DSAH_STROK = new BasicStroke( 613 1.0f , // BasicStroke の幅 614 BasicStroke.CAP_BUTT , // 両端の装飾 615 BasicStroke.JOIN_MITER , // 輪郭線セグメントの接合部の装飾 616 1.0f , // 接合トリミングの制限値 617 new float[] {2.0f, 1.0f} , // 破線パターンを表す配列 618 0.0f // 破線パターン開始位置のオフセット 619 ); 620 621 /** 622 * 時間スケールに対応したSTEP数を管理するための内部クラス 623 * 624 * 時間ヘッダーを表示する場合、ある程度意味のある間隔でラベル表示したいと思います。 625 * 全体の描画領域の長さと、時間当たりのスケールファクター(ピクセル数)から、 626 * ラベルの描画間隔を求めます。 627 * 意味のある間隔は、STEPS で定義し、10分,30分,60分,1/4日,1/2日,1日 まで定義しています。 628 * 一番大きな単位以上の場合は、その最大単位の整数倍を返します。 629 * 630 * 一時間当たりの表示幅を、MIN_PX としていますので、この値以下の間隔では描画されません。 631 * 初期値は、600px を 24時間表示できる 600px/24h = 25px にしています。 632 */ 633 private static final class TimeScaleStep { 634 // 分 分 時 1/4 1/2 1日 635 private static final int[] STEPS = new int[] { 10 , 30 , 60 , 360 , 720 , 1440 }; 636 private static final int MIN_PX = 25; // スケールに対する最小値 637 638 /** 639 * 時間を意味のある範囲の整数として返します。 640 * 641 * 全体の描画領域の長さと、時間当たりのスケールファクター(ピクセル数)から、 642 * 10分,30分,60分,1/4日,1/2日,1日 までの整数値で返します。 643 * 一番大きな単位以上の場合は、その最大単位の整数倍を返します。 644 * 645 * @param timeScale 時間(分)当たりのピクセル数 646 * @return 時間スケールに対応した意味のある範囲の整数 647 */ 648 public static final int getStep( final double timeScale ) { 649 int tmStep = (int)Math.ceil(MIN_PX/(timeScale)); // 整数切り上げ 650 651 for( int i=0; i<STEPS.length; i++ ) { 652 if( tmStep <= STEPS[i] ) { return STEPS[i]; } // 配列の数字に切り上げ 653 } 654 655 // 未設定の場合は、最上位の値の整数倍に切り上げ 656 return (int)Math.ceil( tmStep / STEPS[STEPS.length-1] ) * STEPS[STEPS.length-1]; 657 } 658 } 659 660 /** 661 * 状況コード、ラベル、色を管理するための内部クラス 662 * 663 * 状況に応じたコード、ラベル、色を管理します。 664 * これは、getColor(状況コード,ラベル) または、getColor(状況コード,ラベル,色文字列) で 665 * 要求された情報を内部で管理し、同じコードの場合に同じ色を返します。 666 * また、凡例作成用に、最も文字数が長いラベルを管理します。 667 * 色文字列を指定した場合でも、最初に要求された状況コードに対応する色を返します。 668 * これは、同一状況コードで色違いを作成することができないことを意味します。 669 * 色文字列を指定しない場合は、内部の色配列から、順番に色を割り当てます。 670 * 色を割り当てる順番は、状況コードの発生順です。よって、検索条件によって、 671 * 状況コードの現れる順番が異なると、色も毎回異なることになります。 672 * 673 * 自動割り当ての色は、BLUE,CYAN,GRAY,GREEN,LIGHT_GRAY,MAGENTA,DARK_GRAY,ORANGE,PINK,RED,YELLOW 674 * となっており、それを超えると、RGBをランダムに発生させて色を作成します。 675 * よって、どのような色になるかは全くわかりません。 676 */ 677 private static final class ColorMap { 678 private static final Color[] CLR_ARY = new Color[] { 679 Color.BLUE ,Color.CYAN ,Color.GRAY ,Color.GREEN ,Color.LIGHT_GRAY ,Color.MAGENTA , 680 Color.DARK_GRAY ,Color.ORANGE ,Color.PINK ,Color.RED ,Color.YELLOW 681 }; 682 private int lastCnt = 0; 683 private final Map<String,Object[]> colMap = new TreeMap<String,Object[]>(); 684 private String maxLabel = "" ; // 最大長のラベル 685 private int maxlen = -1 ; // 最大長のラベルのlength() 686 687 /** 688 * 状況コードに対応した色オブジェクトを返します。 689 * 690 * 状況コードが初めて指定された場合は、順番に内部の色を割り当てます。 691 * また、その時のラベルも管理します。ラベルと色のセットは、凡例作成時に利用されます。 692 * 693 * 自動割り当ての色は、BLUE,CYAN,GRAY,GREEN,LIGHT_GRAY,MAGENTA,DARK_GRAY,ORANGE,PINK,RED,YELLOW 694 * となっており、それを超えると、RGBをランダムに発生させて色を作成します。 695 * よって、どのような色になるかは全くわかりません。 696 * 697 * @param fgj 状況コード 698 * @param lbl 状況コードのラベル 699 * @return 状況コードに対応した色オブジェクト 700 */ 701 public Color getColor( final String fgj,final String lbl ) { 702 return getColor( fgj,lbl,null ); 703 } 704 705 /** 706 * 状況コードに対応した色オブジェクトを返します。 707 * 708 * 状況コードが初めて指定された場合は、引数の色文字列の色を割り当てます。 709 * また、その時のラベルも管理します。ラベルと色のセットは、凡例作成時に利用されます。 710 * 711 * 色文字列を指定した場合でも、最初に要求された状況コードに対応する色を返します。 712 * これは、同一状況コードで色違いを作成することができないことを意味します。 713 * 色文字列 が null の場合は、自動割り当てのメソッドと同じです。 714 * よって、色文字列の指定と、自動割り当てが同時に発生すると、異なる状況コードで 715 * 同じ色が指定される可能性がありますので、混在して使用しないでください。 716 * 717 * @param fgj 状況コード 718 * @param lbl 状況コードのラベル 719 * @param colStr 状況コードに対応した色文字列(nullの場合は、自動割り当て) 720 * @return 状況コードに対応した色オブジェクト 721 */ 722 public Color getColor( final String fgj,final String lbl,final String colStr ) { 723 if( fgj == null ) { return LABEL_COLOR; } 724 if( lbl != null ) { 725 int len = lbl.length(); 726 if( len > maxlen ) { maxLabel = lbl; maxlen = len; } 727 } 728 729 Object[] obj = colMap.get( fgj ); 730 if( obj == null ) { 731 obj = new Object[2]; 732 obj[0] = lbl; 733 obj[1] = (colStr != null) ? StringUtil.getColorInstance( colStr ) : uniqColor(); 734 735 colMap.put( fgj,obj ); 736 } 737 738 return (Color)obj[1] ; 739 } 740 741 /** 742 * 内部のシーケンスに対応した、ユニークな色オブジェクトを返します。 743 * 744 * 内部カウンターを+1 しながら、内部の色オブジェクトを返します。 745 * 746 * 自動割り当ての色は、BLUE,CYAN,GRAY,GREEN,LIGHT_GRAY,MAGENTA,DARK_GRAY,ORANGE,PINK,RED,YELLOW 747 * となっており、それを超えると、RGBをランダムに発生させて色を作成します。 748 * よって、どのような色になるかは全くわかりません。 749 * 750 * @return ユニークな色オブジェクト 751 */ 752 public Color uniqColor() { 753 Color col = null; 754 if( lastCnt < CLR_ARY.length ) { 755 col = CLR_ARY[lastCnt++]; 756 } 757 else { 758 int R=(int)(Math.random()*256); 759 int G=(int)(Math.random()*256); 760 int B=(int)(Math.random()*256); 761 col = new Color(R,G,B); 762 } 763 764 return col; 765 } 766 767 /** 768 * 内部で管理している、ラベル(String)と色オブジェクト(Color)の コレクションを返します。 769 * 770 * 内部で管理しているコレクションです。 771 * このコレクションは、状況コードでソートされています。 772 * コレクションの中身は、オブジェクト配列となっており、[0]は、String型のラベル、[1]は、 773 * Color型の色です。 774 * 775 * @return ラベル(String)と色オブジェクト(Color)の コレクション 776 */ 777 public Collection<Object[]> values() { 778 return colMap.values(); 779 } 780 781 /** 782 * 登録されたラベル文字列で、最も文字数が長いラベルを返します。 783 * 784 * 凡例で、ラベルの最大長を求めるのに利用できます。 785 * ただし、簡易的に、length() 計算しているだけなので、英語、日本語混在や、 786 * プロポーショナルフォント使用時の厳密な最大長の文字列ではありません。 787 * 788 * @return 最も文字数が長いラベル 789 */ 790 public String getMaxLengthLabel() { return maxLabel; } 791 } 792 793 /** 794 * 時間文字列を数字に変換します。 795 * 796 * "0800" は、480に、"2100" は、1260 に変換します。 797 * つまり、分単位の値に変換します。秒の単位は、切り捨てます。 798 * ここでは、時間文字列が、08:00 の様に、":" を含む場合は、削除し、 799 * 4ケタ以上の場合は、前から4ケタ分を使います。 800 * 4ケタ以下(1,2,3ケタ)でも、先頭に 0 埋めで、時間文字列にします。 801 * 0ケタ、および、null の場合は、defTm 初期値 を返します。 802 * 803 * @param val 時間文字列の値(0800 など) 804 * @param defTm 引数の時間文字列が null の場合の初期値 805 * 806 * @return 時間文字列を数字に変換した結果( 80 など) 807 */ 808 private int getStr2Time( final String val , final int defTm ) { 809 if( val == null || val.isEmpty() ) { return defTm; } 810 811 // 桁数によって、処理の仕方が変わるため。 812 int rtnInt = 0; 813 switch( val.length() ) { 814 case 1: 815 case 2: rtnInt = Integer.parseInt( val ); break; 816 case 3: rtnInt = Integer.parseInt( val.substring( 0,1 ) )*60 + Integer.parseInt( val.substring( 1,3 ) ); break; 817 default : rtnInt = Integer.parseInt( val.substring( 0,2 ) )*60 + Integer.parseInt( val.substring( 2,4 ) ); break; 818 } 819 820 return rtnInt ; 821 } 822 823 /** 824 * 日時文字列を数字に変換します。 825 * 826 * 日時文字列は、yyyyMMdd または、yyyyMMddhhmmss 形式とします。 827 * これを、エポックタイムからの経過時間の 分単位の値を求めます。 828 * 具体的には、Calendar オブジェクトの getTimeInMillis() の結果を、 829 * 60000 で割り算した値を作成します。 830 * よって、Calendar オブジェクト作成時も、秒の単位は切り捨てます。 831 * 引数が null の場合は、現在時刻より、作成します。 832 * 833 * @param val 日時文字列の値(yyyyMMdd または、yyyyMMddhhmmss 形式 など) 834 * 835 * @return 日時文字列を分換算した数字 836 */ 837 private long getStr2Date( final String val ) { 838 Calendar cal = Calendar.getInstance(); 839 str2DateTime = 0; 840 if( val == null ) { 841 cal.set( Calendar.HOUR_OF_DAY, 0 ); // 5.3.5.0 (2011/05/01) 時間の解決規則が適用されるため、「時」だけは、setメソッドで 0 にセットする。 842 cal.clear( Calendar.MINUTE ); 843 cal.clear( Calendar.SECOND ); 844 cal.clear( Calendar.MILLISECOND ); 845 } 846 else if( val.length() == 8 ) { 847 cal.clear(); 848 cal.set( Integer.parseInt( val.substring( 0,4 ) ) , // 年 849 Integer.parseInt( val.substring( 4,6 ) ) - 1, // 月(0から始まる) 850 Integer.parseInt( val.substring( 6,8 ) ) // 日 851 ); 852 } 853 else { 854 cal.clear(); 855 cal.set( Integer.parseInt( val.substring( 0,4 ) ) , // 年 856 Integer.parseInt( val.substring( 4,6 ) ) - 1, // 月(0から始まる) 857 Integer.parseInt( val.substring( 6,8 ) ) , // 日 858 Integer.parseInt( val.substring( 8,10 ) ) , // 時 859 Integer.parseInt( val.substring( 10,12 ) ) // 分 860 ); 861 str2DateTime = Integer.parseInt( val.substring( 8,10 ) ) * 60 + Integer.parseInt( val.substring( 10,12 ) ) ; 862 } 863 return (cal.getTimeInMillis() / 60000L); // 分単位に変換する。 864 } 865 866 // 5.6.5.0 (2013/06/07) 曜日データを配列で持っておきます。 867 private static final String[] DAY_OF_WEEK_ja = new String[] { "土","日","月","火","水","木","金","土" }; // [0]="土" は、1〜7 の余計算で、 7=0 になる為。 868 869 /** 870 * 数字(分)を時間文字列に変換します。 871 * 872 * 480 は、"08" に、1260 は、"21" に変換します。 873 * 引数の時間は、分を表す整数です。24時間表記であれば、0 〜 1440 の範囲で収まりますが、 874 * 期間が長い場合は、その値を超えます。また、24時間を超える場合は、0 に戻ります。 875 * 文字列にする場合の最小単位は、(時)なので、60(分)で割り算して、余は、切り捨てます。 876 * step は、60(分)単位の表示時に飛ばす数です。step=1 なら、60(分)単位、step=2 なら、120(分) 877 * 単位となります。stepが、24 以下の場合は、そのまま、24時間表記で構いませんが、 878 * それを超えると時間ではなく、日数表記に切り替わります。 879 * 880 * @og.rev 5.6.5.0 (2013/06/07) 月単位の場合は、曜日を表示します。 881 * 882 * @param timeVal 引数の時間整数(分) 883 * @param step 60分単位のステップ数( 10,30,60,720,1440 単位となるように調整) 884 * @param week カレンダクラスの曜日フィールド(DAY_OF_WEEK) 885 * 886 * @return 数字を時間文字列に変換した結果( "08" など) 887 */ 888// private String getTime2Str( final int timeVal, final int step ) { 889 private String getTime2Str( final int timeVal, final int step, final int week ) { 890 891 int dtm = (timeVal / 60 / 24 ); // 日 892 int htm = ((timeVal / 60) % 24 ); // 時(24時間制) 893 int mtm = (timeVal % 60 ); // 分(60分制) 894 895 StringBuilder rtn = new StringBuilder(); 896 897 if( step >= 1440 ) { 898// rtn.append( dtm ).append( "d" ); // 5.6.5.0 (2013/06/07) 相対日付廃止 899 rtn.append( DAY_OF_WEEK_ja[ ( dtm + week ) % 7 ] ); // 曜日を表示 900 } 901 else { 902 if( htm < 10 ) { rtn.append( "0" ); } 903 rtn.append( htm ); 904 905 if( step < 60 ) { 906 rtn.append( ":" ); 907 if( mtm < 10 ) { rtn.append( "0" ); } 908 rtn.append( mtm ); 909 } 910 } 911 912 return rtn.toString(); 913 } 914 915 // 引数に、 を追加する。 916 917 /** 918 * 表示項目の編集(並び替え)が可能かどうかを返します 919 * 920 * @return 表示項目の編集(並び替え)が可能かどうか(false:不可能) 921 */ 922 @Override 923 public boolean isEditable() { 924 return false; 925 } 926}