/* * 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; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.IdScriptableObject; import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.WrappedException; import org.seasar.mayaa.PositionAware; import org.seasar.mayaa.cycle.scope.AttributeScope; import org.seasar.mayaa.engine.specification.PrefixAwareName; import org.seasar.mayaa.engine.specification.QName; import org.seasar.mayaa.impl.CONST_IMPL; import org.seasar.mayaa.impl.cycle.script.AbstractTextCompiledScript; import org.seasar.mayaa.impl.cycle.script.ReadOnlyScriptBlockException; import org.seasar.mayaa.impl.engine.RenderingBrake; import org.seasar.mayaa.impl.engine.specification.PrefixAwareNameImpl; import org.seasar.mayaa.impl.util.ObjectUtil; import org.seasar.mayaa.impl.util.StringUtil; /** * @author Masataka Kurihara (Gluegent, Inc.) */ public class TextCompiledScriptImpl extends AbstractTextCompiledScript { private static final long serialVersionUID = 4793923040332838492L; private final String _sourceName; private final int _lineNumber; private final int _offsetLine; private transient Script _rhinoScript; // for el style -------------- private transient Script _elRhinoScript; private String _elScriptText; private String _elStyleName; private boolean _elStyle; public TextCompiledScriptImpl( String text, PositionAware position, int offsetLine) { super(text); String sourceName = position.getSystemID(); if (position instanceof PrefixAwareName) { PrefixAwareName prefixAwareName = (PrefixAwareName) position; QName qName = prefixAwareName.getQName(); if (CONST_IMPL.URI_MAYAA == qName.getNamespaceURI()) { sourceName += "#" + qName.getLocalName(); } else { sourceName += "#" + PrefixAwareNameImpl.forPrefixAwareNameString(qName, prefixAwareName.getPrefix()); } } _sourceName = sourceName; _lineNumber = position.getLineNumber(); _offsetLine = offsetLine; processText(text); } protected boolean maybeELStyle(String text) { boolean start = true; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (start) { if (Character.isJavaIdentifierStart(c)) { start = false; continue; } } else { if (Character.isJavaIdentifierPart(c)) { continue; } } if (c == '.') { start = true; continue; } return false; } return true; } protected void processText(String text) { if (text == null) { throw new IllegalArgumentException(); } text = text.trim(); String script = text; String name = null; if (maybeELStyle(text)) { int pos = text.lastIndexOf('.'); if (pos != -1 && pos != (text.length() - 1)) { script = text.substring(0, pos); name = text.substring(pos + 1); } } _elScriptText = script; _elStyleName = name; _elStyle = (name != null); } protected Object normalExecute(Context cx, Scriptable scope) { if (cx == null || scope == null) { throw new IllegalArgumentException(); } if (_rhinoScript == null) { _rhinoScript = cx.compileString( getText(), _sourceName, _lineNumber + _offsetLine, null); } return _rhinoScript.exec(cx, scope); } protected Object getELStyleHost(Context cx, Scriptable scope) { if (cx == null || scope == null) { throw new IllegalArgumentException(); } if (StringUtil.isEmpty(_elScriptText)) { return null; } if (_elRhinoScript == null) { _elRhinoScript = cx.compileString( _elScriptText, _sourceName, _lineNumber + _offsetLine, null); } return _elRhinoScript.exec(cx, scope); } protected Object functionExecute( Context cx, Scriptable scope, Object host, Object[] args) { if (cx == null || scope == null || host == null || args == null) { throw new IllegalArgumentException(); } if (host instanceof Scriptable) { Object func = ((Scriptable) host).get(_elStyleName, scope); return ((Function) func).call(cx, scope, (Scriptable) host, args); } Class[] argClasses = getMethodArgClasses(); return ObjectUtil.invoke(host, _elStyleName, args, argClasses); } public Object execute(Object[] args) { Context cx = RhinoUtil.enter(); Object ret = null; try { Scriptable scope = RhinoUtil.getScope(); Object jsRet; if (_elStyle && args != null) { Object host = getELStyleHost(cx, scope); jsRet = functionExecute(cx, scope, host, args); } else { jsRet = normalExecute(cx, scope); } ret = RhinoUtil.convertResult(cx, getExpectedClass(), jsRet); } catch (RhinoException e) { if (e instanceof WrappedException) { WrappedException we = (WrappedException) e; Throwable wrapped = we.getWrappedException(); if (wrapped instanceof RenderingBrake) { RhinoUtil.removeWrappedException(we); } } // エラーとなったソース情報が微妙なので微調整。 // 行番号はスクリプト次第でずれてしまう。 int offsetLine; String message; String sourceName; if (e instanceof JavaScriptException && ((JavaScriptException)e).getValue() instanceof IdScriptableObject) { offsetLine = -1; IdScriptableObject scriptable = (IdScriptableObject) ((JavaScriptException) e).getValue(); Object messageProperty = scriptable.get("message", scriptable); if (messageProperty != Scriptable.NOT_FOUND) { message = messageProperty.toString(); } else { message = scriptable.toString(); } } else { offsetLine = e.lineNumber() - _lineNumber + 1; // one "\n" is added message = e.details() + " in script=\n" + getText(); } if (e.lineSource() == null && message != null) { String[] lines = message.split("\n"); offsetLine = (lines.length > offsetLine) ? offsetLine : _offsetLine; if (offsetLine >= 0 && lines.length > offsetLine) { e.initLineSource(lines[offsetLine]); sourceName = _sourceName; } else { sourceName = e.sourceName(); } } else { sourceName = e.sourceName(); } throw new OffsetLineRhinoException( message, sourceName, e.lineNumber(), e.lineSource(), e.columnNumber(), offsetLine, e.getCause()); } finally { Context.exit(); } return ret; } public boolean isReadOnly() { return _elStyle == false; } public void assignValue(Object value) { if (isReadOnly()) { throw new ReadOnlyScriptBlockException(getScriptText()); } Context cx = RhinoUtil.enter(); try { Scriptable scope = RhinoUtil.getScope(); if (_elStyle) { Object host = getELStyleHost(cx, scope); if (host == null) { scope.put(_elStyleName, scope, value); } else if (host instanceof Scriptable) { ((Scriptable) host).put(_elStyleName, scope, value); } else if (host instanceof AttributeScope) { ((AttributeScope) host).setAttribute(_elStyleName, value); } else { ObjectUtil.setProperty(host, _elStyleName, value); } } else { throw new IllegalStateException(); } } catch (WrappedException e) { RhinoUtil.removeWrappedException(e); } finally { Context.exit(); } } }