テンプレート切り替え機能を拡張する
Mayaaには一つのmayaaで複数のテンプレートを切り替える機能がある。(参考: 3-10. テンプレート切り替え : Documentation - JavaServer Templates "Mayaa") たとえば利用者の使用言語に合わせてその言語用のHTMLを使う、というような用途が想定されているらしい。
使い方としては、たとえばhello.mayaaファイルのm:mayaaタグの属性にm:templateSuffix="${locale}"などと書く事で、locale="en"の時にhello$en.html、locale="ja"の時にhello$ja.htmlを表示に使用することができる。また、hello$○○.htmlが無い場合にはhello.htmlファイルが使用される。
templateSuffixで指定した種類に対応するHTMLファイルが無い場合の挙動だけど、$から.htmlの間が完全一致か、$なしの.htmlかの2ファイルしか選択されないのは少し不便だ。例えばロケールならjaを探す→enを探す→$なしを探す、のように、複数のファイルを指定した順で検索して欲しい場合もある。携帯電話用のサイトなら、DoCoMo QVGA用→DoCoMo全機種用→携帯全機種用などのように検索して欲しい。そこで、テンプレート切り替え部分のクラスを拡張して、カスケードが可能なようにしてみた。
機能の説明
m:templateSuffixで指定した文字列を"_"で分解して、前にある要素を優先してテンプレートを順に検索する。
例えば、hello.mayaaのm:templateSuffixに"mobile_docomo_high"を指定した場合、以下の順序で検索をおこない、見つかったファイルをテンプレートに使用する。
ソースコードと設定
(動作確認は一応したけど実戦投入はまだです。)
org.seasar.mayaa.impl.engine.PageImplのサブクラスを作り、getTemplate()メソッドをオーバーライドして処理を差し替える。
package sample.mayaa.engine; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.seasar.mayaa.engine.Template; import org.seasar.mayaa.impl.engine.PageImpl; import org.seasar.mayaa.impl.engine.PageNotFoundException; import org.seasar.mayaa.impl.util.StringUtil; public class SuffixCascadePageImpl extends PageImpl { private static final String SEPARATOR = "_"; private static final long serialVersionUID = -4792634398812735416L; @Override public Template getTemplate(String originalSuffix, String extension) { if (originalSuffix == null) { originalSuffix = ""; } if (extension == null) { extension = ""; } if (QM_MAYAA.getLocalName().equals(extension)) { return null; } Template template = null; Iterator<String> suffixes = getSuffixes(originalSuffix).iterator(); while (suffixes.hasNext()) { template = getTemplateFromFixedSuffix(suffixes.next(), extension); if (template != null) { return template; } } if (StringUtil.hasValue(originalSuffix)) { template = getTemplateFromFixedSuffix("", extension); } if (template == null) { throw new PageNotFoundException(getPageName(), extension); } return template; } /** * 指定された文字列をセパレータ'_'で切り分け、種類文字列の候補を再帰的に生成する。 * 例えば"mobile_docomo_high"を指定した場合、以下の順序で文字列が含まれるListを戻す。 * <ol> * <li>"mobile_docomo_high"</li> * <li>"mobile_docomo"</li> * <li>"mobile_hight"</li> * <li>"mobile"</li> * <li>"docomo_high"</li> * <li>"docomo"</li> * <li>"high"</li> * </ol> * @param suffix 指定された種類文字列 * @return 種類文字列候補のList */ protected List<String> getSuffixes(final String suffix) { int pos = suffix.indexOf(SEPARATOR); if (pos == -1) { return Arrays.asList(new String[] {suffix}); } String head = suffix.substring(0, pos); String rest = suffix.substring(pos + 1); List<String> subList = getSuffixes(rest); List<String> results = new ArrayList<String>(); for (String tail : subList) { results.add(head + SEPARATOR + tail); } results.add(head); results.addAll(subList); return results; } }
META-INF/org.seasar.mayaa.provider.ServiceProviderに上のクラスを指定する。
(下のXMLのpageClassの行)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE provider PUBLIC "-//The Seasar Foundation//DTD Mayaa Provider 1.0//EN" "http://mayaa.seasar.org/dtd/mayaa-provider_1_0.dtd"> <provider> <engine> <parameter name="convertCharset" value="true"/> <parameter name="pageClass" value="sample.mayaa.engine.SuffixCascadePageImpl"/> </engine> </provider>
おしまい。
使い方サンプル
ServletのFilterなどで、User-Agentなどから割り出した端末スペックをリクエストにセット
// 例 public class DeviceFilter implements Filter { .... public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // agent判定 String carrier; // "docomo" or "au" or "thirdforce" String resolution; // "high" or "low" .... // agent設定 request.setAttribute("agent", "mobile_" + carrier + "_" + resolution); chain.doFilter(request, response); }
mayaaファイルでは、リクエストの"agent"値をtemplateSuffixに指定
例: hello.mayaa
<?xml version="1.0" encoding="Windows-31J"?> <m:mayaa xmlns:m="http://mayaa.seasar.org" m:templateSuffix="${agent}"> .... </m:mayaa>
これでファイルを配置するかどうかで、hoge$mobile_docomo.html、hoge$mobile_au.htmlなどのキャリア毎の出し分けも、hoge$mobile_hight.html, hoge$mobile_low.htmlなどの画面サイズでの出し分けも(あるいはその組み合わせも)出来る。