JSFの実装の一つである、MyFaces(http://myfaces.apache.org/)で提供されている、
カレンダーコンポーネントを、JRA式の土曜日始まりに出来るか?
という素朴な疑問から始まった、MyFacesカスタマイズめもです。
ちなみに.NETのカレンダーコンポーネントは、
FirstDayOfWeek属性を指定することで、何曜日始まりにもできるそうです。
この記事では、以下のことを行います。
[前提]
[ソースのダウンロード]
MyFacesのソースのダウンロードは、ここになります。
ここにいって、myfaces-1.0.9-src.zipもしくは、myfaces-1.0.9-src.tgzをダウンロードします。
そして、適当な解凍ツールで解凍し、展開ディレクトリを適当な場所に置きます。
[コンパイル環境の構築]
MyFacesをコンパイルするには、いくつかの依存するライブラリを集めなくてはなりません。
ほんとはMavenで出来るのかも知れませんが、知らないので集めます。
myfaces.jarをコンパイルするのに必要なJar構成は以下のようになります。
portlet-api-1.0.jar以外は、以下のURLから入手できます。
http://cvs.sourceforge.net/viewcvs.py/myfaces/myfaces/lib/
portlet-api-1.0.jarは、色々探した結果見つからなかったので、
http://fisheye.cenqua.com/viewrep/ws/ws-wsrp4j/lib/shared/portlet-api-1.0.jar
から入手(rawのリンクをクリック)したのですが、myfaces-1.0.9-app.zip(サンプル集)の
myfaces-blank-example.warの/WEB-INF/libの中にも入っています。
(参考)
servlet-2.3-jsp-1.2.jarは、Tomcat5に含まれている、
servlet-api.jarとjsp-api.jarの二つでも代替可能です。
その場合、build.xmlとbuild.default.propertiesを両方読むように書き換える必要があります。
[コンパイル(Ant)の実行]
コマンドラインで、myfaces-1.0.9-src/buildディレクトリを開きます。
そして、以下のantコマンドを実行します。
ant myfaces-jar
成功すると、myfaces-1.0.9-src/lib以下にmyfaces.jarが作成されます。
うまくいかない場合は、Jar構成が間違っていないかどうか確認してください。
[カレンダーを土曜日始まりにする]
ソースツリーをCalendarというファイル名で検索してみると、
myfaces-1.0.9-src\src\components\org\apache\myfaces\custom\calendar
のフォルダが発見されます。
このフォルダにあるJavaソースは以下の3つになります。
そしてさらに見ていくと、239行目にある、
int weekStartsAtDayIndex = mapCalendarDayToCommonDay(timeKeeper.getFirstDayOfWeek());
の1行が、週の始めの曜日を取得する処理であることが分かります。
ここで取得しているweekStartsAtDayIndexは、152行目で取得しているweekDaysの配列のインデックスであって、
java.util.Calendarの定数とは一致していませんので、注意が必要です。
つまり、weekStartsAtDayIndexを土曜日に変えれば、
カレンダーが土曜日始まりになると考えられるため、
以下のように修正します。
これでコンパイルして、myfaces.jarを生成します。
配布されたmyfaces.jarと入れ替えて、カレンダーを表示してみてください。
土曜日始まりに表示されたでしょうか?
■土曜日始まりなイメージ
[カレンダーを指定曜日始まりにする]
inputCalendarタグに、firstDayOfWeek属性を追加して、
例えば、
・TLDファイルの修正
カスタムタグの定義ファイルを修正します。
myfaces-1.0.9-src\tlds\myfaces_ext.tld
myfaces-1.0.9-src\myfaces_ext_sf.tldってのもあるが、よく分かりません。。
sfはSourceForgeの略であるようですが。
中をみていくと、434行目からinputCalendarタグの設定が書いてあります。
<!-- calendar --> <tag> <name>inputCalendar</name> <tag-class>org.apache.myfaces.custom.calendar.HtmlInputCalendarTag</tag-class> <body-content>JSP</body-content> <description> Provides a calendar. </description> &ui_input_attributes; ... 以下略
属性の定義がずらずらと下に続いているので、
ここにfirstDayOfWeekを追加します。
<attribute> <name>popupSelectDateMessage</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description>Set the string for "Select [date] as date" (do not replace [date], it will be replaced by the current date).</description> </attribute> <!-- 追加 --> <attribute> <name>firstDayOfWeek</name> <required>false</required> <rtexprvalue>false</rtexprvalue> <description>Set the number for First Day Of Week. 0:Monday ... 6:Sunday</description> </attribute> <!-- 追加終わり -->
これでTLDの修正はOKです。
次にソースコードを修正します。
・ソースコードの修正
まずは、HtmlInputCalendarTag.javaです。
このクラスは、タグの動作を決めるクラスのようですが、
JSFおよびMyFacesのフレームワークに隠蔽されて、
ほとんど処理らしい処理がありません。
以下のような修正を行えばよいです。
・フィールド定義の追加 (104行目付近)
private String _popupSelectDateMessage = null; // 追加 private String _firstDayOfWeek = null; // 追加終わり
・releaseメソッドの修正 (130行目付近)
_visibleOnUserRole = null; // 追加 _firstDayOfWeek = null; // 追加終わり
・setPropertiesメソッドの修正 (154行目付近)
setStringProperty(component,"popupSelectDateMessage",_popupSelectDateMessage); // 追加 setStringProperty(component,"firstDayOfWeek",_firstDayOfWeek); // 追加終わり
・setメソッドの追加 (249行目付近)
public void setPopupWeekString(String popupWeekString) { _popupWeekString = popupWeekString; } // 追加 public void setFirstDayOfWeek(String firstDayOfWeek) { _firstDayOfWeek = firstDayOfWeek; } // 追加終わり
要は、フィールドを定義して、初期化処理とセッターを追加したというところでしょう。
次に、HtmlInputCalendar.javaを修正します。
このクラスは、HtmlCalendarRendererに実際に渡されるもので、
恐らく、タグライブラリのDTO的役割を果たしているものと思われます。
修正は、HtmlInputCalendarTag.javaとほぼ同じです。
・フィールド定義の追加 (56行目付近)
private String _popupSelectDateMessage = null; // 追加 private String _firstDayOfWeek = null; // 追加終わり
・ゲッター/セッターの追加 (284行目付近)
public String getPopupSelectDateMessage() { if (_popupSelectDateMessage != null) return _popupSelectDateMessage; ValueBinding vb = getValueBinding("popupSelectDateMessage"); return vb != null ? (String)vb.getValue(getFacesContext()) : null; } // 追加 public void setFirstDayOfWeek(String firstDayOfWeek) { _firstDayOfWeek = firstDayOfWeek; } public String getFirstDayOfWeek() { if(_firstDayOfWeek != null) return _firstDayOfWeek; ValueBinding vb = getValueBinding("firstDayOfWeek"); return vb != null ? (String)vb.getValue(getFacesContext()) : null; } // 追加終わり
・saveStateメソッドの修正 (295, 314行目付近)
// 修正 //Object values[] = new Object[19]; Object values[] = new Object[20]; // 修正終わり values[0] = super.saveState(context); ... 省略 values[18] = _popupSelectDateMessage; // 追加 values[19] = _firstDayOfWeek; // 追加終わり return ((Object) (values));
・restoreStateメソッドの修正 (339行目付近)
_popupSelectDateMessage = (String)values[18]; // 追加 _firstDayOfWeek = (String)values[19]; // 追加終わり
最後に、HtmlCalendarRenderer.javaを修正します。
ここまでの作業で、inputCalendarタグのfirstDayOfWeek属性にセットされた値が、
HtmlInputCalendarのfirstDayOfWeekフィールドにセットされるので、
これを取り出して利用するようコードを修正します。(239行目付近)
// 修正 int weekStartsAtDayIndex = mapCalendarDayToCommonDay(timeKeeper.getFirstDayOfWeek()); // もともとのコード if(inputCalendar.getFirstDayOfWeek() != null) { try { int firstDayOfWeek = Integer.parseInt(inputCalendar.getFirstDayOfWeek()); if(firstDayOfWeek >= 0 && firstDayOfWeek <= 6) { weekStartsAtDayIndex = firstDayOfWeek; } } catch(NumberFormatException e) { // デフォルトを採択 } } // 修正終わり
ここまで修正できたら、コンパイルしてmyfaces.jarを生成してください。
配布されたmyfaces.jarと入れ替えて、カレンダータグを以下のようにしてみてください。
■木曜日始まりなイメージ