GSPにpageEncodingディレクティブを!(Grails 0.5)
昨日のpageEncodingディレクティブを自分で作ってみよう。ということで、取っ掛かりは先日の日記のGroovyPagesTemplateEngineクラスあたりから。プラグインのdoWithSpringクロージャをみるとjspViewResolverもプロパティとして持っているし、getResourceForUri(String)をオーバライドするとちゃんとビューが変わるのでここから後の処理であると予想する。
GroovyPagesTemplateEngineクラスのソースによると、getResourceForUri(String)を呼び出しているのは、calculateLineNumbersForPage(ServletContext,String)とcreateTemplate(String)だけだ。また、GrailsViewResolverの場合も、GrailsViewResolver#loadView()→GroovyPageView#renderMergedOutputModel()→GroovyPageView#renderWithTemplateEngine()→GroovyPagesTemplateEngine#createTemplate()と最終的にGroovyPagesTemplateEngine#createTemplate(String)で処理が行われているっぽい。
GroovyPagesTemplateEngine#createTemplate(String)のソースは以下のとおり。
public Template createTemplate(String uri) { return createTemplate(getResourceForUri(uri)); }
むう、先日のロケール対応処理が2重に実行されそうな気がしなくもない。今度チェックしよう。で、GroovyPagesTemplateEngine#createTemplate(Resource)のソースは以下のとおり。
public Template createTemplate(Resource resource) { if(resource == null) { GrailsWebRequest webRequest = getWebRequest(); throw new GroovyPagesException("No Groovy page found for URI: " + getCurrentRequestUri(webRequest.getCurrentRequest())); } String name = establishPageName(resource, null); if(pageCache.containsKey(name)) { GroovyPageMetaInfo meta = (GroovyPageMetaInfo)pageCache.get(name); if(isGroovyPageReloadable(resource, meta)) { try { return createTemplateWithResource(resource); } catch (IOException e) { throw new GroovyPagesException("I/O error reading stream for resource ["+resource+"]: " + e.getMessage(),e); } } else { return new GroovyPageTemplate(meta); } } else { try { return createTemplateWithResource(resource); } catch (IOException e) { throw new GroovyPagesException("I/O error reading stream for resource ["+resource+"]: " + e.getMessage(),e); } } }
createTemplateWithResource(Resource)かな?
private Template createTemplateWithResource(Resource resource) throws IOException { InputStream in = resource.getInputStream(); try { return createTemplate(in, resource, null); } finally { in.close(); } }
リソースをオープンしているな。さらに奥に。
protected Template createTemplate(InputStream inputStream, Resource resource, String pageName) { GroovyPageMetaInfo metaInfo = buildPageMetaInfo(inputStream, resource, pageName); return new GroovyPageTemplate(metaInfo); }
ストリームを使用しているのはbuildPageMetaInfo()だ。次。
protected GroovyPageMetaInfo buildPageMetaInfo(InputStream inputStream, Resource res, String pageName) { String name = establishPageName(res, pageName); long lastModified = establishLastModified(res); Parse parse; try { parse = new Parse(name, inputStream); } catch (IOException e) { throw new GroovyPagesException("I/O parsing Groovy page ["+(res != null ? res.getDescription() : name)+"]: " + e.getMessage(),e); } InputStream in = parse.parse(); // Make a new metaInfo GroovyPageMetaInfo metaInfo = createPageMetaInfo(parse, lastModified, in); metaInfo.setPageClass( compileGroovyPage(in, name) ); pageCache.put(name, metaInfo); return metaInfo; }
ストリームを使用しているのはParseクラスしかない。名前どおりこのクラスでGSPファイルを解析しているのだろう。org.codehaus.groovy.grails.web.pages.Parseクラスも見る必要があるな。で、このクラスのコンストラクタは、
public Parse(String name, InputStream in) throws IOException { scan = new Scan(readStream(in)); makeName(name); } // Parse()
Scanクラスというのが出てきた。と、その前にreadStream()でストリームの内容を読み込んでいる?この処理はというと、
private String readStream(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { byte[] buf = new byte[8192]; for (;;) { int read = in.read(buf); if (read <= 0) break; out.write(buf, 0, read); } return out.toString(); } finally { out.close(); in.close(); } } // readStream()
なるほど、ストリームから読み出して全部文字列にしてるのね。最後の
return out.toString();
でストリームの内容はデフォルトエンコーディングとして扱われているというわけだ。ここを
return out.toString("UTF-8");
とかにすれば、ファイルは全てUTF-8として扱われることになるんだろう。今回は、pageEncodingディレクティブで指定したいので、固定でUTF-8とか指定できない。さあ、どうしようか。
Parseクラスのサブクラスを作っていじろうにも、ParseクラスのコンストラクタでreadStream()が呼ばれちゃうんだよなorz。さらによくよく見てみると、Scanクラスも、GroovyPageMetaInfoもpackage privateになってる!!protected GroovyPageMetaInfo buildPageMetaInfo()ってなってるのにぃ。一体どうしろというのか。
ストリームの内容を自前で読み込んで、デフォルトエンコーディングに変換してから渡すとかすればよさそうだけど、デフォルトエンコーディングに変換できない文字がGSPファイルに存在していたらダメだよなぁ。GroovyPageMetaInfo、Scan、Parseに相当するものを全部自前で作らないとダメっぽい。
どーする、俺!?