/*
* 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();
}
}
}