/* * Copyright 2004-2012 the Seasar Foundation and the Others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.seasar.mayaa.impl.engine; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.seasar.mayaa.cycle.ServiceCycle; import org.seasar.mayaa.cycle.script.CompiledScript; import org.seasar.mayaa.engine.Engine; import org.seasar.mayaa.engine.Page; import org.seasar.mayaa.engine.Template; import org.seasar.mayaa.engine.TemplateRenderer; import org.seasar.mayaa.engine.processor.ChildEvaluationProcessor; import org.seasar.mayaa.engine.processor.IterationProcessor; import org.seasar.mayaa.engine.processor.ProcessStatus; import org.seasar.mayaa.engine.processor.ProcessorTreeWalker; import org.seasar.mayaa.engine.processor.TemplateProcessor; import org.seasar.mayaa.engine.processor.TryCatchFinallyProcessor; import org.seasar.mayaa.impl.CONST_IMPL; import org.seasar.mayaa.impl.cycle.CycleUtil; import org.seasar.mayaa.impl.engine.processor.ElementProcessor; import org.seasar.mayaa.impl.engine.specification.SpecificationUtil; import org.seasar.mayaa.impl.provider.ProviderUtil; import org.seasar.mayaa.impl.util.StringUtil; import org.seasar.mayaa.source.SourceDescriptor; /** * @author Masataka Kurihara (Gluegent, Inc.) */ public class RenderUtil implements CONST_IMPL { private static final ProcessStatus SKIP_BODY = ProcessStatus.SKIP_BODY; private static final ProcessStatus EVAL_BODY_INCLUDE = ProcessStatus.EVAL_BODY_INCLUDE; private static final ProcessStatus SKIP_PAGE = ProcessStatus.SKIP_PAGE; private static final ProcessStatus EVAL_PAGE = ProcessStatus.EVAL_PAGE; private static final ProcessStatus EVAL_BODY_AGAIN = ProcessStatus.EVAL_BODY_AGAIN; private static final ProcessStatus EVAL_BODY_BUFFERED = ProcessStatus.EVAL_BODY_BUFFERED; private RenderUtil() { // no instantiation. } public static boolean isEvaluation(TemplateProcessor current) { return current instanceof ChildEvaluationProcessor && ((ChildEvaluationProcessor) current).isChildEvaluation(); } public static ChildEvaluationProcessor getEvaluation( TemplateProcessor current) { return (ChildEvaluationProcessor) current; } public static boolean isIteration(TemplateProcessor current) { return current instanceof IterationProcessor && ((IterationProcessor) current).isIteration(); } public static IterationProcessor getIteration( TemplateProcessor current) { return (IterationProcessor) current; } public static boolean isDuplicated(TemplateProcessor current) { return current instanceof ElementProcessor && ((ElementProcessor) current).isDuplicated(); } public static boolean isTryCatchFinally(TemplateProcessor current) { if (current instanceof TryCatchFinallyProcessor) { TryCatchFinallyProcessor tryCatchFinallyProcessor = (TryCatchFinallyProcessor) current; return tryCatchFinallyProcessor.canCatch(); } return false; } public static TryCatchFinallyProcessor getTryCatchFinally( TemplateProcessor current) { return (TryCatchFinallyProcessor) current; } public static void saveToCycle(ProcessorTreeWalker current) { ServiceCycle cycle = CycleUtil.getServiceCycle(); cycle.setProcessor(current); if (current instanceof TemplateProcessor) { TemplateProcessor proc = (TemplateProcessor) current; cycle.setOriginalNode(proc.getOriginalNode()); cycle.setInjectedNode(proc.getInjectedNode()); } else if (current instanceof Template) { Template temp = (Template) current; cycle.setOriginalNode(temp); cycle.setInjectedNode(temp); } } // main rendering method public static ProcessStatus renderTemplateProcessor( Page topLevelPage, TemplateProcessor current) { if (current == null) { throw new IllegalArgumentException(); } saveToCycle(current); ServiceCycle cycle = CycleUtil.getServiceCycle(); ProcessStatus ret = EVAL_PAGE; boolean buffered = false; setCurrentProcessor(current); Engine engine = ProviderUtil.getEngine(); SpecificationUtil.execEvent(engine, QM_BEFORE_RENDER_PROCESSOR); try { SpecificationUtil.startScope(current.getVariables()); ProcessStatus startRet = EVAL_BODY_INCLUDE; startRet = current.doStartProcess(topLevelPage); if (startRet == SKIP_PAGE) { return SKIP_PAGE; } if (startRet == EVAL_BODY_BUFFERED && isEvaluation(current)) { buffered = true; ChildEvaluationProcessor processor = getEvaluation(current); processor.setBodyContent(cycle.getResponse().pushNoFlushWriter()); processor.doInitChildProcess(); } if (startRet == EVAL_BODY_INCLUDE || startRet == EVAL_BODY_BUFFERED) { try { buffered = renderTemplateProcessorChildren( topLevelPage, current, buffered); } catch (SkipPageException e) { return SKIP_PAGE; } } saveToCycle(current); if (buffered) { cycle.getResponse().popWriter(); getIteration(current).doAfterChildProcess(); } ret = current.doEndProcess(); } catch (RuntimeException e) { if (isTryCatchFinally(current)) { getTryCatchFinally(current).doCatchProcess(e); } else { throw e; } } finally { SpecificationUtil.endScope(); if (isTryCatchFinally(current)) { getTryCatchFinally(current).doFinallyProcess(); } setCurrentProcessor(current); SpecificationUtil.execEvent(engine, QM_AFTER_RENDER_PROCESSOR); setCurrentProcessor(null); } return ret; } /** * ボディのレンダリング中に以後のレンダリングを中止する例外。 * @author Koji Suga (Gluegent Inc.) */ public static class SkipPageException extends Exception { private static final long serialVersionUID = -2166811321531507794L; public SkipPageException() { super(); } } /** * プロセッサのボディをレンダリングする処理。 * * @param topLevelPage レンダリングの起点となるページ * @param current 現在のプロセッサ * @param oldBuffered メソッド開始時のバッファするか否かフラグ * @return バッファするか否か * @throws SkipPageException */ public static boolean renderTemplateProcessorChildren( Page topLevelPage, TemplateProcessor current, boolean oldBuffered) throws SkipPageException { ServiceCycle cycle = CycleUtil.getServiceCycle(); ProcessStatus afterRet; boolean buffered = oldBuffered; do { for (int i = 0; i < current.getChildProcessorSize(); i++) { ProcessorTreeWalker child = current.getChildProcessor(i); if (child instanceof TemplateProcessor) { TemplateProcessor childProc = (TemplateProcessor) child; final ProcessStatus childRet = renderTemplateProcessor(topLevelPage, childProc); if (childRet == SKIP_PAGE) { throw new SkipPageException(); } } else { throw new IllegalStateException("child processor type error"); } } afterRet = SKIP_BODY; saveToCycle(current); if (isIteration(current)) { if (buffered) { buffered = false; cycle.getResponse().popWriter(); } afterRet = getIteration(current).doAfterChildProcess(); ProcessorTreeWalker parent = current.getParentProcessor(); if (parent instanceof TemplateProcessor) { TemplateProcessor parentProc = (TemplateProcessor) parent; if (afterRet == EVAL_BODY_AGAIN && isDuplicated(parentProc)) { saveToCycle(parentProc); parentProc.doEndProcess(); parentProc.doStartProcess(null); if (oldBuffered) { buffered = true; ChildEvaluationProcessor processor = getEvaluation(current); processor.setBodyContent(cycle.getResponse().pushNoFlushWriter()); processor.doInitChildProcess(); } } } } } while (afterRet == EVAL_BODY_AGAIN); return buffered; } private static final String CURRENT_PROCESSOR_KEY = "__currentProcessor__"; protected static TemplateProcessor getCurrentProcessor() { return (TemplateProcessor)CycleUtil.getRequestScope().getAttribute(CURRENT_PROCESSOR_KEY); } protected static void setCurrentProcessor(TemplateProcessor processor) { CycleUtil.getRequestScope().setAttribute(CURRENT_PROCESSOR_KEY, processor); } // Rendering entry point public static ProcessStatus renderProcessorTree( Page topLevelPage, ProcessorTreeWalker root) { for (int i = 0; i < root.getChildProcessorSize(); i++) { ProcessorTreeWalker child = root.getChildProcessor(i); if (child instanceof TemplateProcessor) { TemplateProcessor childProc = (TemplateProcessor) child; final ProcessStatus childRet = renderTemplateProcessor(topLevelPage, childProc); if (childRet == SKIP_PAGE) { return SKIP_PAGE; } } else { throw new IllegalStateException("child processor is not a templateProcessor"); } } return EVAL_PAGE; } public static void saveToCycle(Page page) { ServiceCycle cycle = CycleUtil.getServiceCycle(); cycle.setOriginalNode(page); cycle.setInjectedNode(page); } protected static Template getTemplate(String requestedSuffix, Page page, String suffix, String extension) { if (EngineUtil.getMayaaExtensionName().equals(extension)) { SourceDescriptor source = page.getSource(); if (source.exists() == false) { String pageName = page.getPageName(); throw new PageNotFoundException(pageName, extension); } } else { if (StringUtil.isEmpty(suffix)) { if (StringUtil.isEmpty(requestedSuffix)) { CompiledScript script = page.getSuffixScript(); suffix = (String) script.execute(null); } else { suffix = requestedSuffix; } } return page.getTemplate(suffix, extension); } return null; } /** * ページをレンダリングする。 * * @param fireEvent beforeRender等のイベントを実行するかどうか。 * layoutのinsertのように実行しない場合はfalseを渡すこと。 * @param renderer レンダリングを実行するオブジェクト * @param variables beforeRender時に変数として宣言するもの * @param topLevelPage リクエストのあったページ * @param requestedSuffix リクエストされたsuffix * @param extension リクエストのあった拡張子 * @return 後の処理を表すステータス */ public static ProcessStatus renderPage(boolean fireEvent, TemplateRenderer renderer, Map variables, Page topLevelPage, String requestedSuffix, String extension) { if (renderer == null || topLevelPage == null) { throw new IllegalArgumentException(); } Page page = topLevelPage; String suffix = null; saveToCycle(page); List pageStack = new LinkedList(); List templateStack = new LinkedList(); do { // stack for afterRender event. pageStack.add(0, page); if (fireEvent) { SpecificationUtil.startScope(variables); SpecificationUtil.execEvent(page, QM_BEFORE_RENDER); } Template template = getTemplate(requestedSuffix, page, suffix, extension); if (template != null) { // LIFO access templateStack.add(0, template); } suffix = page.getSuperSuffix(); extension = page.getSuperExtension(); page = page.getSuperPage(); variables = null; } while(page != null); ProcessStatus ret = null; if (templateStack.size() > 0) { Template[] templates = (Template[]) templateStack.toArray(new Template[templateStack.size()]); ret = renderer.renderTemplate(topLevelPage, templates); saveToCycle(page); } if (fireEvent) { for (int i = 0; i < pageStack.size(); i++) { page = (Page) pageStack.get(i); saveToCycle(page); SpecificationUtil.execEvent(page, QM_AFTER_RENDER); SpecificationUtil.endScope(); } } return ret; } }