/* * 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.processor; import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.seasar.mayaa.cycle.Response; import org.seasar.mayaa.cycle.ServiceCycle; import org.seasar.mayaa.cycle.scope.RequestScope; import org.seasar.mayaa.engine.Engine; import org.seasar.mayaa.engine.Page; import org.seasar.mayaa.engine.processor.InformalPropertyAcceptable; import org.seasar.mayaa.engine.processor.ProcessStatus; import org.seasar.mayaa.engine.processor.ProcessorProperty; import org.seasar.mayaa.engine.processor.VirtualPropertyAcceptable; import org.seasar.mayaa.engine.specification.PrefixAwareName; import org.seasar.mayaa.impl.CONST_IMPL; import org.seasar.mayaa.impl.cycle.CycleUtil; import org.seasar.mayaa.impl.cycle.DefaultCycleLocalInstantiator; import org.seasar.mayaa.impl.engine.EngineUtil; import org.seasar.mayaa.impl.engine.PageImpl; import org.seasar.mayaa.impl.engine.RenderNotCompletedException; import org.seasar.mayaa.impl.engine.RenderUtil; import org.seasar.mayaa.impl.engine.specification.SpecificationUtil; import org.seasar.mayaa.impl.provider.ProviderUtil; import org.seasar.mayaa.impl.util.StringUtil; /** * @author Masataka Kurihara (Gluegent, Inc.) */ public class InsertProcessor extends TemplateProcessorSupport implements CONST_IMPL, InformalPropertyAcceptable, VirtualPropertyAcceptable { private static final long serialVersionUID = -945900326182620192L; private static final String RENDERING_INSERT_CHAIN = InsertProcessor.class.getName() + "#renderingInsertChain"; private static final String INSERT_PARAMS = InsertProcessor.class.getName() + "#insertParams"; static { CycleUtil.registVariableFactory(RENDERING_INSERT_CHAIN, new DefaultCycleLocalInstantiator() { public Object create(Object[] params) { return new Stack/*<InsertRenderingParams>*/(); } }); CycleUtil.registVariableFactory(INSERT_PARAMS, new DefaultCycleLocalInstantiator() { public Object create(Object owner, Object[] params) { return new Stack/*<InsertRenderingParams>*/(); } }); } class PathPart { String _pathValue; String _pageName; String _suffix; String _extension; boolean _needAdjustPath; } private ProcessorProperty _path; private ProcessorProperty _name; private transient PathPart _pathPart; private List/*<Serializable(ProcessorProperty or PrefixAwareName)>*/ _attributes; /** * 相対パスを解決、分解して拡張子などをあらかじめ取得する。 */ public void initialize() { if (_path != null && _path.getValue() != null) { if (_path.getValue().isLiteral()) { _pathPart = parsePath(_path.getValue().getScriptText()); } } } private PathPart parsePath(String path) { Engine engine = ProviderUtil.getEngine(); String suffixSeparator = engine.getParameter(SUFFIX_SEPARATOR); PathPart result = new PathPart(); if (path != null) { String[] pagePath = StringUtil.parsePath(path, suffixSeparator); String sourcePath = EngineUtil.getSourcePath(getStaticParentProcessor()); result._pathValue = path; result._pageName = StringUtil.adjustRelativePath(sourcePath, pagePath[0]); result._suffix = pagePath[1]; result._extension = pagePath[2]; if (path.startsWith("/") == false && path.startsWith("./") == false) { result._needAdjustPath = true; } } return result; } // MLD property, required public void setPath(ProcessorProperty path) { if (path == null || path.getValue() == null) { throw new IllegalArgumentException(); } String pathScript = path.getValue().getScriptText(); if (StringUtil.isEmpty(pathScript)) { throw new IllegalArgumentException(); } _path = path; } // MLD property public void setName(ProcessorProperty name) { if (name != null) { _name = name; } } // MLD method public void addInformalProperty(PrefixAwareName name, Serializable attr) { if (_attributes == null) { _attributes = new ArrayList(); } _attributes.add(attr); } /** * @deprecated 1.1.9 代わりに{@link #getInformalPropertyClass()}を使う */ public Class getPropertyClass() { return getInformalPropertyClass(); } /** * @deprecated 1.1.9 代わりに{@link #getInformalPropertyClass()}を使う */ public Class getExpectedClass() { return getInformalExpectedClass(); } public Class getInformalPropertyClass() { return ProcessorProperty.class; } public Class getInformalExpectedClass() { return Object.class; } /** * VirtualPropertyもInformalPropertyとして扱います。 * @param name プロパティ名。 * @param property プロパティ値。 */ public void addVirtualProperty(PrefixAwareName name, Serializable attr) { addInformalProperty(name, attr); } /** * VirtualPropertyもInformalPropertyとして扱います。 */ public Class getVirtualPropertyClass() { return getInformalPropertyClass(); } /** * VirtualPropertyもInformalPropertyとして扱います。 */ public Class getVirtualExpectedClass() { return getInformalExpectedClass(); } public List getInformalProperties() { if (_attributes == null) { _attributes = new ArrayList(); } return _attributes; } private PathPart getPathPart() { PathPart pathPart = _pathPart; if (pathPart == null) { if (_path == null || StringUtil.isEmpty(_path.getValue().getScriptText())) { return new PathPart(); } String path = StringUtil.valueOf(_path.getValue().execute(null)); if (StringUtil.isEmpty(path)) { return new PathPart(); } pathPart = parsePath(path); } return pathPart; } private Page pathPartToPage(PathPart pathPart) { if (pathPart._pageName == null) { return null; } if (pathPart._needAdjustPath) { // "/" 始まりでも "./" 始まりでもない場合は相対パス解決をする String sourcePath = EngineUtil.getSourcePath(getStaticParentProcessor()); return ProviderUtil.getEngine().getPage( StringUtil.adjustRelativePath(sourcePath, "./" + pathPart._pageName)); } return ProviderUtil.getEngine().getPage(pathPart._pageName); } /** * pathを指定されていない場合(レイアウト側)はnullを返す。 * pathが指定されている場合(コンポーネント利用)はEngineからPageを * 取得して返す。その際、必要ならばパスの自動解決をする。 * * @return ページオブジェクト */ protected Page getPage() { return pathPartToPage(getPathPart()); } protected Stack getRenderingParams() { return (Stack) CycleUtil.getLocalVariable(INSERT_PARAMS, this, null); } protected InsertRenderingParams pushRenderingParams() { InsertRenderingParams newParams = new InsertRenderingParams(); return (InsertRenderingParams) getRenderingParams().push(newParams); } protected InsertRenderingParams peekRenderingParams() { return (InsertRenderingParams) getRenderingParams().peek(); } protected InsertRenderingParams popRenderingParams() { return (InsertRenderingParams) getRenderingParams().pop(); } public Map getRenderingParameters() { InsertRenderingParams params = peekRenderingParams(); if (params.isRendering()) { return params.getParams(); } return null; } protected void invokeBeforeRenderComponent(Page component) { InsertRenderingParams params = peekRenderingParams(); params.setRendering(true); params.setStackComponent(PageImpl.getCurrentComponent()); params.setCurrentComponent(component); PageImpl.setCurrentComponent(component); SpecificationUtil.execEvent(ProviderUtil.getEngine(), QM_BEFORE_RENDER_COMPONENT); } protected void invokeAfterRenderComponent() { InsertRenderingParams params = peekRenderingParams(); PageImpl.setCurrentComponent(params.getCurrentComponent()); SpecificationUtil.execEvent(ProviderUtil.getEngine(), QM_AFTER_RENDER_COMPONENT); PageImpl.setCurrentComponent(params.getStackComponent()); params.setRendering(false); params.clear(); } public ProcessStatus doStartProcess(Page topLevelPage) { PathPart pathPart = getPathPart(); Page renderPage = pathPartToPage(pathPart); boolean fireEvent = true; // layoutからcontentを呼ぶ場合 if (renderPage == null) { ServiceCycle cycle = CycleUtil.getServiceCycle(); renderPage = topLevelPage; RequestScope request = cycle.getRequestScope(); pathPart._suffix = request.getRequestedSuffix(); pathPart._extension = request.getExtension(); fireEvent = false; } if (renderPage == null) { throw new IllegalStateException(); } Map properties = new LinkedHashMap(getInformalProperties().size()); for (int i = 0; i < getInformalProperties().size(); i++) { Object object = getInformalProperties().get(i); if (object instanceof ProcessorProperty) { ProcessorProperty prop = (ProcessorProperty) object; properties.put( prop.getName().getQName().getLocalName(), prop.getValue().execute(null)); } } Map params = pushRenderingParams().getParams(); params.clear(); params.putAll(properties); properties.clear(); invokeBeforeRenderComponent(renderPage); try { ProcessStatus ret; getRenderingInsertChain().push(this); try { String name; if (_name == null) { name = ""; } else { name = StringUtil.valueOf(_name.getValue().execute(null)); } ComponentRenderer renderer = new ComponentRenderer(this, name); ret = RenderUtil.renderPage(fireEvent, renderer, getVariables(), renderPage, pathPart._suffix, pathPart._extension); } finally { getRenderingInsertChain().pop(); } if (ret == null) { Response response = CycleUtil.getResponse(); if (response.getWriter().isDirty() == false) { throw new RenderNotCompletedException( renderPage.getPageName(), pathPart._extension); } } if (ret == ProcessStatus.EVAL_PAGE) { ret = ProcessStatus.SKIP_BODY; } return ret; } catch(RuntimeException e) { invokeAfterRenderComponent(); throw e; } } protected static Stack getRenderingInsertChain() { return (Stack) CycleUtil.getGlobalVariable(RENDERING_INSERT_CHAIN, null); } public static InsertProcessor getRenderingCurrent() { if (getRenderingInsertChain().size() == 0) { return null; } return (InsertProcessor) getRenderingInsertChain().peek(); } public ProcessStatus doEndProcess() { invokeAfterRenderComponent(); popRenderingParams(); return super.doEndProcess(); } // for serialize private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { out.defaultWriteObject(); } private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { in.defaultReadObject(); } }