スポンサー広告 - スポンサーサイト

--/--/-- (--) --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【プログラミング】JAVA - POI備忘録 其の参

2011/06/04 (土) 21:51

だいぶ前にメモしてあったものを記録。

POI 3.7でのお話。続き。
Office2007が登場し、エクセルの新しいファイル形式が誕生しました。
それに伴いPOIもバージョンアップし、新・旧両方のファイル形式に対応しています。
結局それぞれに対応したクラスが別々に存在するわけですが、
何と共通のインターフェースが存在しており、
これを使うことによってファイル形式の違いを意識せずに開発できるようになっているようです。

要するに、
List list = new ArrayList();
とかするのと同じですね。

Google先生に聞いても古い情報ばっかりであまり情報が得られなかったので、実際に弄ってみますた。

/*
* 例外処理とかストリームの扱い(開けたり閉めたり)は省略
*/

// 既に存在するファイルを読み込む場合
Workbook book = WorkbookFactory.create(new FileInputStream("fileName"));

// 新規作成の場合は形式に合ったクラスを使って作成する
Workbook hssfBook = new HSSFWorkbook();
Workbook xssfBook = new XSSFWorkbook();
どうやらファイル形式を識別する術があるようです。
org.apache.poi.ss.usermodel.WorkbookFactory クラスは、
POIFSFileSystem.hasPOIFSHeader(InputStream)
POIXMLDocument.hasOOXMLHeader(InputStream)
の両メソッドでファイル形式を検査している模様です。
中身で何をやっているかまでは見ていませんが(汗)

取得したオブジェクト
org.apache.poi.ss.usermodel.Workbook
もインターフェース。
この後は
org.apache.poi.ss.usermodel.Sheet
org.apache.poi.ss.usermodel.Row
org.apache.poi.ss.usermodel.Cell
org.apache.poi.ss.usermodel.CellStyle
org.apache.poi.ss.usermodel.Font
等のインターフェースを駆使してエクセルの捜査を行っていくわけですね。
どうやらこの辺の共通部品が
org.apache.poi.ss.usermodel.*
のクラス群のようです。

形式に依存する処理を行いたい場合は、対応する形式の方のクラスにキャストして使えばよいでしょう。


【プログラミング】JAVA - POI備忘録 其の弐

2010/11/30 (火) 21:38

POI 3.7でのお話。続き。
座標の指定をVBAチックにしたい。
そんなときに便利なのが
org.apache.poi.ss.util.CellReference
org.apache.poi.ss.util.CellRangeAddress
クラス。

単一のセルの場合
CellReference cr = new CellReference("A1");
System.out.println(cr.getRow() + ":" + cr.getCol());
出力結果は「0:0」

ちなみに、列を表すアルファベットをインデックスに変換したい場合。
int ret = CellReference.convertColStringToIndex("A");
System.out.println(ret);
出力結果は「0」

その逆は
String ret = CellReference.convertNumToColString(0);
System.out.println(ret);
出力結果は「A」

こんな感じで相互に変換が可能。
これらのメソッドの存在を知らずに自力で変換ロジックを書いたのは内緒。

さらにVBAチックにセルの範囲指定が可能。
CellRangeAddress address = CellRangeAddress.valueOf("A1:D5");
int firstRow = address.getFirstRow();
int lastRow = address.getLastRow();
int firstColumn = address.getFirstColumn();
int lastColumn = address.getLastColumn();
System.out.println(firstRow + ":" + lastRow + ":" + firstColumn + ":" + lastColumn);
出力結果は「0:4:0:3」
2重ループで回せば該当セル全てに対して処理が可能。
これは備忘録 其の壱 でも何気なく使ってました。

インデックスから座標文字列の取得も可能。
CellRangeAddress address = new CellRangeAddress(0, 4, 0, 3);
System.out.println(address.formatAsString());
出力結果は「A1:D5」

座標の指定がエクセルを操作する感覚でできるので便利だと思う。
結局はインデックスに変換して処理することにはなるのだけども。
なんだかんだと色々用意されているのね。

【プログラミング】JAVA - log4jのNDCとMDC

2010/11/28 (日) 21:04

NDCとは「Nested Diagnostic Context」の略。
org.apache.log4j.NDC クラスを使う。

NDC.push("hoge1");
とかすると、ログ出力レイアウト上の「%x」が置き換わる。
スタックするので、さらに
NDC.push("hoge2");
とかすると、「%x」は「hoge1 hoge2」になる。
この状態で
NDC.pop();
すると、末尾のデータが無くなり「%x」は「hoge1」になる。
さらに
NDC.pop();
すると、データが無くなり「%x」は空文字になる。
不要になったら
NDC.remove();
で消し去る。
この機能はマルチスレッドに対応しているのがポイント。

で、何に使うかというと、
WEBアプリケーションのログで、1回のリクエストで複数のログを出力する場合、
アクセスが同時に複数あると、どのログがどのリクエストの操作なのかわからなくなる。
ログにセッションIDを出力することができれば、そのセッションIDだけを抽出することにより、
特定のリクエストを解析することが可能になり、便利なはず。
リクエストと言ったが、端末というかブラウザというか、複数の異なるセッションIDからのアクセスを識別するということになるかな。

具体的な使用例としては、サーブレットによる実装だろう。
以下のような感じ。
必要なアクセスに対してマッピングを設定すれば、ログに容易にセッションIDを出力可能になる。
package test.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.NDC;

public class Log4jFilter implements Filter {

public void init(FilterConfig conf) throws ServletException {
// 特にやる事なし。
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

// セッションがスタートしていることが前提。
NDC.push(((HttpServletRequest) request).getSession().getId());

try {
chain.doFilter(request, response);
} finally {
NDC.remove();
}

}

public void destroy() {
// 特にやる事なし。
}

}

ちなみに、MDCとは「Mapped Diagnostic Context」の略。
スタックではなくマッピングにて同様の処理を行う。
MDC.put(key, value);
レイアウト上では「%X{key}」が置き換わる。

【プログラミング】JAVA - POI備忘録 其の壱

2010/11/27 (土) 17:46

POI 3.7でのお話。
ちょっと弄る機会があったので。

セルに対する背景色とか、枠線とか、所謂「スタイル」について。
セルそれぞれが設定値を持つものだと思いきや、
なんとスタイルはブックが持つものでした。
で、セルはどのスタイルが設定されているかの参照情報を持つという仕組み。

なので、同じスタイルが設定されているセルが複数存在する場合は、
その中のとあるセルのスタイルを変更すると、該当するすべてのセルに影響を及ぼす。
で、このスタイルは1つのブックに最大4000個までが上限となっている。

これはPOIの仕様というわけではなく、そもそものエクセルの仕様であるので、
POIはその仕様通りの動きをしているだけなのでした。
全然知らなかったぜぇ。エクセルめぇ。

すっかり騙されてしまったので、記録しておく。

例えば、以下の自前のメソッドは上記の問題を孕む。
/**
* 指定範囲のセル全てに指定の背景色を設定する
* @param sheet 操作対象のシート
* @param coordinate 操作対象の座標範囲(例 AW18:BG28)
* @param color 背景色
*/
public void setBackgroundColor(Sheet sheet, String coordinate, short color) {

CellRangeAddress address = CellRangeAddress.valueOf(coordinate);
int firstRow = address.getFirstRow();
int lastRow = address.getLastRow();
int firstColumn = address.getFirstColumn();
int lastColumn = address.getLastColumn();

for (int i = firstRow; i <= lastRow; i++) {
for (int j = firstColumn; j <= lastColumn; j++) {
Row row = CellUtil.getRow(i, sheet);
Cell cell = CellUtil.getCell(row, j);

CellStyle style = cell.getCellStyle();
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
style.setFillBackgroundColor(color);

cell.setCellStyle(style);
}
}
}
操作対象のセルからスタイルを取り出して更新しているって処理。
エクセルの仕様を理解していないとこれで問題無いように思えるが、
同じスタイルを参照しているセル全てに影響を及ぼすので、
場合によっては全てのセルの見た目が変わってしまう可能性がある。(むしろあった!)

そこで便利なメソッドを見つけた。
CellUtil.setCellStyleProperty である。
これを使って以下のように変更する。
/**
* 指定範囲のセル全てに指定の背景色を設定する
* @param sheet 操作対象のシート
* @param coordinate 操作対象の座標範囲(例 AW18:BG28)
* @param color 背景色
*/
public void setBackgroundColor(Sheet sheet, String coordinate, short color) {

CellRangeAddress address = CellRangeAddress.valueOf(coordinate);
int firstRow = address.getFirstRow();
int lastRow = address.getLastRow();
int firstColumn = address.getFirstColumn();
int lastColumn = address.getLastColumn();

for (int i = firstRow; i <= lastRow; i++) {
for (int j = firstColumn; j <= lastColumn; j++) {
Row row = CellUtil.getRow(i, sheet);
Cell cell = CellUtil.getCell(row, j);
CellUtil.setCellStyleProperty(cell, sheet.getWorkbook(), CellUtil.FILL_PATTERN, CellStyle.SOLID_FOREGROUND);
CellUtil.setCellStyleProperty(cell, sheet.getWorkbook(), CellUtil.FILL_FOREGROUND_COLOR, color);
}
}
}
CellUtil.setCellStyleProperty が何をやっているかというと、
変更後のスタイルと同様の設定内容のスタイルが既にブックに存在しないかをチェックし、(最大で4000ループってことになるか)
存在していればそれを使う。
無ければ新たにスタイルを作成し、それを使う。
さらに、元々のスタイルに設定を追加する処理になっているので、
例えば上記のメソッドの場合だと、元々枠線が設定されていたセルが対象の場合、
枠線の設定は残しつつ背景が追加される。要するに追記できるって感じ。
嗚呼…便利だね♪

CellUtil は結構興味深いので、どんなメソッドが用意されているのかチェックしておくことをお勧めする。

【プログラミング】JAVA - JAVAのパスが設定されない

2010/10/09 (土) 15:51

一昔前はJAVAのインストール時には環境変数でパスを通す必要があったけども、
いま時はそんなことしなくても良いらしい。

仕事で使っているマシンはXPなのだが、確かに環境変数にパスの設定をしていない。
が、コマンドプロンプトで「java」とか打つと、オプションの説明がずらずら出てくる。

なんでかと調べてみたところ、レジストリにJAVAのインストール情報が記録されていた。
複数のバージョンが入っていても、最新の実行ファイルのパスがわかるようになっている。
で、何と「C:\Windows\System32」の下に「java.exe」がいた。
こいつを実行すると、レジストリから最新の実行ファイルのパスを取得してそいつを実行している模様。
「C:\Windows\System32」にはパスが通っているのが当たり前なので、わざわざJAVAの実行ファイルのパスを通す必要がなくなるわけだ。
レジストリの書き換えで実行するバージョンを変えているということだなやぁ。

ちなみに、結局動かせるのJREの方なので、JDKをインストールしたときにはきちっとパスを通してあげないと、JDK固有のツール(native2ascii等)が使いにくい。
※レジストリを弄ってしまえば期待通りに動かすことは可能。

いつからこうなったのかは知らないが、便利なんだか不便なんだか。

もちろんWindows向けのお話です。
検索フォーム
RSSリンクの表示
カレンダー
03 | 2017/04 | 05
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 - - - - - -
月別アーカイブ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。