/* * 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.cycle.script.rhino.direct; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.mozilla.javascript.NativeObject; import org.seasar.mayaa.PositionAware; import org.seasar.mayaa.cycle.ServiceCycle; import org.seasar.mayaa.cycle.scope.AttributeScope; import org.seasar.mayaa.cycle.script.CompiledScript; import org.seasar.mayaa.impl.cycle.CycleUtil; import org.seasar.mayaa.impl.cycle.script.rhino.NativeServiceCycle; import org.seasar.mayaa.impl.cycle.script.rhino.WalkStandardScope; import org.seasar.mayaa.impl.provider.ProviderUtil; /** * スクリプトがGetterScriptだと解釈できるなら対応するGetterScriptを * 作成して返すFactory。 * GetterScriptと呼んでいるのは、attributeまたはpropertyの取得が連なっている * だけのスクリプトのこと。 * TODO 完成させる * * @author Koji Suga (Gluegent Inc.) */ public class GetterScriptFactory { /** GetterScriptとして解釈するパターン */ protected static final Pattern GETTER_SCRIPT_PATTERN = Pattern.compile("\\s*([a-zA-Z_][a-zA-Z0-9_]*)" + "(\\s*\\.\\s*[a-zA-Z_][a-zA-Z0-9_]*|" + "\\s*\\[\\s*'\\s*[a-zA-Z0-9_]*\\s*'\\s*\\]|" + "\\s*\\[\\s*\"\\s*[a-zA-Z0-9_]*\\s*\"\\s*\\]" + "\\s*)*[\\s;]*"); /** 個別getterのパターン */ protected static final Pattern GETTER_PATTERN = Pattern.compile("\\s*\\.\\s*([a-zA-Z_][a-zA-Z0-9_]*)|" + "\\s*\\[\\s*'\\s*([a-zA-Z0-9_]*)\\s*'\\s*\\]|" + "\\s*\\[\\s*\"\\s*([a-zA-Z0-9_]*)\\s*\"\\s*\\]" + "\\s*"); /** * 対応するGetterScriptを生成して返します。 * GetterScriptでない場合はnullを返します。 * * @param script 対象とするScript * @param position スクリプトのソースを表す情報 * @param offsetLine スクリプトの行番号 * @return 対応するGetterScriptまたはnull */ public static CompiledScript create( String script, PositionAware position, int offsetLine) { String[] splited = splitScope(script); if (splited == null) { return null; } return createGetterScript(script, position, offsetLine, splited); } /** * スクリプトをスコープと変数名、プロパティ名に分割します。 * * @param script * @return [スコープ名, 変数名, プロパティ名]のString配列。GetterScriptでない場合はnull。 */ protected static String[] splitScope(String script) { Matcher fullMatcher = GETTER_SCRIPT_PATTERN.matcher(script); if (fullMatcher.matches() == false) { return null; } List result = new ArrayList(); String first = fullMatcher.group(1); if (isScopeName(first)) { result.add(first); } else { result.add(null);// standardScope result.add(first); } Matcher matcher = GETTER_PATTERN.matcher(script); int nextIndex = 0; while (matcher.find(nextIndex)) { result.add(getFromRange(matcher, 1, 3)); nextIndex = matcher.end(); } return (String[]) result.toArray(new String[result.size()]); } private static String getFromRange(Matcher matcher, int start, int end) { String result = null; int index = start; do { result = matcher.group(index++); } while (result == null && index <= end); return result; } /** * 対応するGetterScriptを生成して返します。 * 対応していないスコープ名の場合はnullを返します。 * * @param script 対象とするScript * @param position スクリプトのソースを表す情報 * @param offsetLine スクリプトの行番号 * @param scopeName 対象とするスコープ名 * @param attributeName 変数名 * @return 対応するGetterScriptまたはnull */ protected static CompiledScript createGetterScript( String script, PositionAware position, int offsetLine, String[] params) { // TODO 無限段対応 if (params.length > 3) { return null;// 無限段対応までは上限が3段階 } String scopeName = params[0]; String attributeName = null; String propertyName = null; if (params.length > 1) { attributeName = params[1]; } if (params.length > 2) { propertyName = params[2]; } if (containsRhinoSpecialProperty(attributeName, propertyName)) { return null; } if (scopeName == null || WalkStandardScope.SCOPE_NAME.equals(scopeName)) { return new StandardGetterScript( script, position, offsetLine, scopeName, attributeName, propertyName); } else if (ServiceCycle.SCOPE_PAGE.equals(scopeName) || "this".equals(scopeName)) { return new PageGetterScript( script, position, offsetLine, scopeName, attributeName, propertyName); } else if (ServiceCycle.SCOPE_REQUEST.equals(scopeName)) { return new RequestGetterScript( script, position, offsetLine, scopeName, attributeName, propertyName); } else if (ServiceCycle.SCOPE_SESSION.equals(scopeName)) { return new SessionGetterScript( script, position, offsetLine, scopeName, attributeName, propertyName); } else if (ServiceCycle.SCOPE_APPLICATION.equals(scopeName)) { return new ApplicationGetterScript( script, position, offsetLine, scopeName, attributeName, propertyName); } else { Iterator it = ProviderUtil.getScriptEnvironment().iterateAttributeScope(); while (it.hasNext()) { AttributeScope scope = (AttributeScope) it.next(); if (scope.getScopeName().equals(scopeName)) { return new AttributeScopeGetterScript( script, position, offsetLine, scopeName, attributeName, propertyName); } } } return null; } /** * 予約語かどうかを判定します。 * ここでいう予約語とは、ServiceCycleに対する呼び出しになるものか、 * あるいはRhinoの予約語です。 * * @param word 判定する語 * @return 予約語ならtrue */ protected static boolean isReserved(String word) { if ("java".equals(word) || "Packages".equals(word)) { return true; } ServiceCycle cycle = CycleUtil.getServiceCycle(); NativeServiceCycle nc = new NativeServiceCycle(new NativeObject(), cycle); return nc.hasMember(word, nc); } /** * Rhinoの特殊なプロパティのうち、Javaでエミュレートしづらいものが含まれている * かどうかを判定します。 * __parent__はスコープの場合に限り対応可能。__proto__は対応不能。 * * @param attributeName 属性名 * @param propertyName プロパティ名 * @return 特殊な名前を含むならtrue */ protected static boolean containsRhinoSpecialProperty( String attributeName, String propertyName) { return "__proto__".equals(attributeName) || "__proto__".equals(propertyName); } /** * スコープとして認識するかどうかを判定します。 * ただしここで認識したスコープがすべて変換対象となるわけではありません。 * * @param name 判定する名前 * @return スコープとして認識する名前であればtrue */ protected static boolean isScopeName(String name) { if (CycleUtil.getStandardScope().contains(name) || "this".equals(name)) { return true; } Iterator it = ProviderUtil.getScriptEnvironment().iterateAttributeScope(); while (it.hasNext()) { AttributeScope scope = (AttributeScope) it.next(); if (scope.getScopeName().equals(name)) { return true; } } return false; } }