/* * 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.specification; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import org.seasar.mayaa.cycle.ServiceCycle; import org.seasar.mayaa.cycle.script.CompiledScript; import org.seasar.mayaa.engine.specification.Namespace; import org.seasar.mayaa.engine.specification.NodeAttribute; import org.seasar.mayaa.engine.specification.NodeTreeWalker; import org.seasar.mayaa.engine.specification.PrefixAwareName; import org.seasar.mayaa.engine.specification.PrefixMapping; import org.seasar.mayaa.engine.specification.QName; import org.seasar.mayaa.engine.specification.Specification; import org.seasar.mayaa.engine.specification.SpecificationNode; import org.seasar.mayaa.engine.specification.URI; import org.seasar.mayaa.impl.CONST_IMPL; import org.seasar.mayaa.impl.cycle.CycleUtil; import org.seasar.mayaa.impl.cycle.script.LiteralScript; import org.seasar.mayaa.impl.cycle.script.ScriptUtil; import org.seasar.mayaa.impl.provider.ProviderUtil; import org.seasar.mayaa.impl.util.StringUtil; /** * Specificationに関わるユーティリティメソッドをまとめたクラスです。 * * @author Masataka Kurihara (Gluegent, Inc.) */ public class SpecificationUtil implements CONST_IMPL { private static EventScriptEnvironment _eventScripts = new EventScriptEnvironment(); public static final PrefixMapping XML_DEFAULT_PREFIX_MAPPING = PrefixMappingImpl.getInstance("xml", CONST_IMPL.URI_XML); public static final PrefixMapping HTML_DEFAULT_PREFIX_MAPPING = PrefixMappingImpl.getInstance("", CONST_IMPL.URI_HTML); public static final PrefixMapping XHTML_DEFAULT_PREFIX_MAPPING = PrefixMappingImpl.getInstance("", CONST_IMPL.URI_XHTML); private SpecificationUtil() { // no instantiation. } /** * nodeの属性のうちqNameで表されるものの値を返します。 * 見つからない場合はnullを返します。 * * @param node 対象とするノード * @param qName 属性名 * @return 属性値 */ public static String getAttributeValue( SpecificationNode node, QName qName) { NodeAttribute nameAttr = node.getAttribute(qName); if (nameAttr != null) { return nameAttr.getValue(); } return null; } /** * currentのparentを辿り、Specificationが見つかった場合それを返します。 * 見つからない場合はnullを返します。 * * @param current 対象とするノード * @return Specification */ public static Specification findSpecification(NodeTreeWalker current) { while (current instanceof Specification == false) { current = current.getParentNode(); if (current == null) { return null; } } return (Specification) current; } /** * ServiceCycleで現在処理中のノードからparentを辿り、Specificationが * 見つかった場合それを返します。 * 見つからない場合はnullを返します。 * * @return Specification */ public static Specification findSpecification() { ServiceCycle cycle = CycleUtil.getServiceCycle(); NodeTreeWalker current = cycle.getOriginalNode(); // リソースアクセス中にredirectしても対応できるようにする。 if (current != null) { return findSpecification(current); } return null; } /** * currentが持つm:mayaaノードを探し、もしあればm:mayaaノードの属性のうち * qNameで表されるものの値を返します。 * 見つからない場合はnullを返します。 * * @param current 対象とするノード * @param qName 属性名 * @return 属性値 */ public static SpecificationNode getMayaaNode(NodeTreeWalker current) { Specification specification = findSpecification(current); for (Iterator it = specification.iterateChildNode(); it.hasNext();) { SpecificationNode node = (SpecificationNode) it.next(); if (node.getQName().equals(QM_MAYAA)) { return node; } } return null; } /** * currentが持つm:mayaaノードを探し、もしあればm:mayaaノードの属性のうち * qNameで表されるものの値を返します。 * 見つからない場合はnullを返します。 * * @param current 対象とするノード * @param qName 属性名 * @return 属性値 */ public static String getMayaaAttributeValue( NodeTreeWalker current, QName qName) { SpecificationNode mayaa = getMayaaNode(current); if (mayaa != null) { String value = getAttributeValue(mayaa, qName); if (value != null) { return value; } } return null; } /** * nodeの子要素である文字ノードをまとめた文字列を取得します。 * 文字ノード以外を含む場合、{@link IllegalChildNodeException} が発生します。 * CDATAはその子を文字ノードと見なします。 * * @param node 文字ノードを子として持つノード * @return 文字ノードをまとめた文字列 * @throws IllegalChildNodeException 文字ノード以外を含む場合 */ public static String getNodeBodyText(SpecificationNode node) { StringBuffer buffer = new StringBuffer(256); for (Iterator it = node.iterateChildNode(); it.hasNext();) { SpecificationNode child = (SpecificationNode) it.next(); QName qName = child.getQName(); if (QM_CDATA.equals(qName)) { buffer.append(getNodeBodyText(child)); } else if (QM_CHARACTERS.equals(qName)) { buffer.append(SpecificationUtil.getAttributeValue( child, QM_TEXT)); } else { String name = child.getPrefix() + ":" + qName.getLocalName(); throw new IllegalChildNodeException(name); } } return buffer.toString(); } /** * スクリプト実行環境のスコープを初期化します。 */ public static void initScope() { ProviderUtil.getScriptEnvironment().initScope(); } /** * スクリプト実行環境の新規スコープを開始します。 * @param variables 初期変数 */ public static void startScope(Map variables) { ProviderUtil.getScriptEnvironment().startScope(variables); } /** * スクリプト実行環境の現在スコープを終了します。 */ public static void endScope() { ProviderUtil.getScriptEnvironment().endScope(); } public static void execEvent(Specification spec, QName eventName) { if (eventName == null) { throw new IllegalArgumentException(); } SpecificationNode mayaa = getMayaaNode(spec); if (mayaa != null) { if (_eventScripts.isCached(spec, eventName)) { _eventScripts.execEventScripts(spec, eventName); } else { List eventChilds = new ArrayList(); for (Iterator it = mayaa.iterateChildNode(); it.hasNext();) { SpecificationNode child = (SpecificationNode) it.next(); if (eventName.equals(child.getQName())) { eventChilds.add(child); } } _eventScripts.execEventScriptsAndCache(spec, eventName, eventChilds); } } } // factory methods ---------------------------------------------- public static Namespace createNamespace() { return new NamespaceImpl(); } public static Namespace getFixedNamespace(Namespace original) { return original; //return NamespaceImpl.getInstance(original); } public static QName createQName(String localName) { return createQName(URI_MAYAA, localName); } public static QName createQName( URI namespaceURI, String localName) { return QNameImpl.getInstance(namespaceURI, localName); } public static PrefixMapping createPrefixMapping( String prefix, URI namespaceURI) { return PrefixMappingImpl.getInstance(prefix, namespaceURI); } public static PrefixMapping createPrefixMapping( String prefixAndNamespaceURI) { return PrefixMappingImpl.revertStringToMapping(prefixAndNamespaceURI); } public static QName parseQName(String qName) { if (StringUtil.hasValue(qName) && qName.charAt(0) == '{') { int end = qName.indexOf('}'); if (end != -1 && end < qName.length() - 1) { String namespaceURI = qName.substring(1, end).trim(); String localName = qName.substring(end + 1).trim(); if (StringUtil.hasValue(namespaceURI) && StringUtil.hasValue(localName)) { return QNameImpl.getInstance(createURI(namespaceURI), localName); } } } throw new IllegalArgumentException(qName); } public static PrefixAwareName createPrefixAwareName(QName qName) { return createPrefixAwareName(qName, ""); } public static PrefixAwareName createPrefixAwareName(QName qName, String prefix) { return PrefixAwareNameImpl.getInstance(qName, prefix); } public static SpecificationNode createSpecificationNode( QName qName, String systemID, int lineNumber, boolean onTemplate, int sequenceID) { SpecificationNodeImpl node = new SpecificationNodeImpl(qName); node.setSequenceID(sequenceID); node.setSystemID(systemID); node.setLineNumber(lineNumber); node.setOnTemplate(onTemplate); return node; } public static URI createURI(String uri) { return URIImpl.getInstance(uri); } // script cache ---------------------------------------------- protected static class EventScriptEnvironment { // WeakHashMap<Specification, Map<QName, List<CompiledScript>>> private Map _mayaaScriptCache = Collections.synchronizedMap(new WeakHashMap()); protected CompiledScript compile(String text) { if (StringUtil.hasValue(text)) { ScriptUtil.assertSingleScript(text); return ScriptUtil.compile(text, Void.class); } return LiteralScript.NULL_LITERAL_SCRIPT; } protected List getEventScripts(Specification spec, QName eventName) { Map events = (Map)_mayaaScriptCache.get(spec); if (events == null) { return null; } return (List)events.get(eventName); } public boolean isCached(Specification spec, QName eventName) { return getEventScripts(spec, eventName) != null; } public boolean execEventScriptsAndCache( Specification spec, QName eventName, List eventChilds) { if (eventChilds == null) { throw new NullPointerException(); } Map events = (Map)_mayaaScriptCache.get(spec); if (events == null) { events = new HashMap(); _mayaaScriptCache.put(spec, events); } List scripts = new ArrayList(); ServiceCycle cycle = CycleUtil.getServiceCycle(); for (Iterator it = eventChilds.iterator(); it.hasNext();) { SpecificationNode child = (SpecificationNode) it.next(); NodeTreeWalker save = cycle.getInjectedNode(); try { cycle.setInjectedNode(child); String bodyText = getNodeBodyText(child); bodyText = ScriptUtil.getBlockSignedText(bodyText); CompiledScript script = compile(bodyText); scripts.add(script); script.execute(null); } finally { cycle.setInjectedNode(save); } } if (scripts.size() == 0) { events.put(eventName, Collections.EMPTY_LIST); } else { events.put(eventName, scripts); } return true; } public boolean execEventScripts( Specification spec, QName eventName) { List scripts = getEventScripts(spec, eventName); if (scripts == null) { return false; } ServiceCycle cycle = CycleUtil.getServiceCycle(); for (Iterator it = scripts.iterator(); it.hasNext();) { NodeTreeWalker save = cycle.getInjectedNode(); try { ((CompiledScript) it.next()).execute(null); } finally { cycle.setInjectedNode(save); } } return true; } } public static Namespace copyNamespace(Namespace original) { return NamespaceImpl.copyOf(original); } public static URI getDefaultTemplateNamespace(String systemID) { return null; } }