reinhard 2003/09/08 15:56:34
Modified:
src/blocks/scratchpad/java/org/apache/cocoon/components/flow/javascript/fom
AO_FOM_JavaScriptInterpreter.java
JavaScriptAspectWeaver.java
Log:
- add support for new interception events:
* stopExecution()
* continueExecution()
--> they are called before/after a function that creates a continuation is
called
The interpreter is configured with a list of all functions that are known to
create
continuations (cocoon.sendPageAndWait(), woody.send())
- improved Javadocs
- updated TODO list
- rename of some methods and variables to be more consistent
Revision Changes Path
1.2 +43 -13
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/flow/javascript/fom/AO_FOM_JavaScriptInterpreter.java
Index: AO_FOM_JavaScriptInterpreter.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/flow/javascript/fom/AO_FOM_JavaScriptInterpreter.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- AO_FOM_JavaScriptInterpreter.java 6 Sep 2003 13:23:30 -0000 1.1
+++ AO_FOM_JavaScriptInterpreter.java 8 Sep 2003 22:56:34 -0000 1.2
@@ -87,8 +87,21 @@
import org.mozilla.javascript.tools.shell.Global;
/**
- * Interface with the JavaScript interpreter.
- *
+ * <p>Interface with the JavaScript interpreter.</p>
+ * <p>This version of the JavaScript interpreter provides enhanced
+ * functionality and supports interception.</p>
+ *
+ * <p>Changes:
+ * <ul>
+ * <li>Use of the AO_FOM_Cocoon object encapsulating the Cocoon object.
+ * All references to the FOM_Cocoon object had to be changed.
+ * </li>
+ * <li>Additional configurations</li>
+ * <li>adding the <code>JavaScriptAspectWeaver</code> to the SourceEntry
+ * object if interceptions are enabled</li>
+ * </ul>
+ * </p>
+ *
* @author <a href="mailto:[EMAIL PROTECTED]">Ovidiu Predescu</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Marcus Crafter</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Christopher Oliver</a>
@@ -159,7 +172,7 @@
public Source getSource() {
return source;
}
-
+ // (RPO) added/changed by interception layer
private JavaScriptAspectWeaver aspectWeaver = null;
public void setAspectWeaver( JavaScriptAspectWeaver aspectWeaver ) {
@@ -178,6 +191,7 @@
}
return script;
}
+ // -- end
}
/**
@@ -185,9 +199,10 @@
*
*/
Map compiledScripts = new HashMap();
-
JSErrorReporter errorReporter;
+
boolean enableDebugger = false;
+
/**
* JavaScript debugger: there's only one of these: it can debug multiple
* threads executing JS code.
@@ -217,9 +232,14 @@
return debugger;
}
- public void configure(Configuration config)
- throws ConfigurationException
- {
+ // (RPO) added by interception layer
+ Configuration stopExecutionFunctionsConf = null;
+ boolean copyResultScript = false;
+ // --end
+
+ public void configure(Configuration config)
+ throws ConfigurationException {
+
super.configure(config);
String loadOnStartup
@@ -234,8 +254,14 @@
enableDebugger = true;
}
- isInterceptionEnabled = config.getChild( "enable-interception"
).getValueAsBoolean( true );
-
+ // (RPO) added by interception layer
+ isInterceptionEnabled =
+ config.getChild( "enable-interception" ).getValueAsBoolean( true
);
+ stopExecutionFunctionsConf =
+ config.getChild( "cont-creating-functions" );
+ copyResultScript =
+ config.getChild( "copy-result-script" ).getValueAsBoolean( false
);
+ // --end
}
public void initialize()
@@ -458,13 +484,16 @@
entry = new ScriptSourceEntry(src);
compiledScripts.put(sourceURI, entry);
}
+ // (RPO) added/changed by interception layer
// add interception support
if( isInterceptionEnabled ) {
JavaScriptAspectWeaver aspectWeaver = new
JavaScriptAspectWeaver();
aspectWeaver.setEnvironment( environment );
aspectWeaver.enableLogging( this.getLogger() );
+ aspectWeaver.setStopExecutionFunctionsConf(
this.stopExecutionFunctionsConf );
entry.setAspectWeaver( aspectWeaver );
- }
+ }
+ // --end
// Compile the script if necessary
entry.getScript(context, this.scope, needsRefresh);
}
@@ -512,7 +541,7 @@
return compiledScript;
}
}
-
+ // (RPO) added/changed by interception layer
private Script compileScript( Context cx, Scriptable scope,
Source src, JavaScriptAspectWeaver
aspectWeaver)
throws Exception {
@@ -544,7 +573,8 @@
}
return compiledScript;
}
-
+ // -- end
+
/**
* Calls a JavaScript function, passing <code>params</code> as its
* arguments. In addition to this, it makes available the parameters
1.2 +185 -37
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/flow/javascript/fom/JavaScriptAspectWeaver.java
Index: JavaScriptAspectWeaver.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/flow/javascript/fom/JavaScriptAspectWeaver.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- JavaScriptAspectWeaver.java 6 Sep 2003 13:23:30 -0000 1.1
+++ JavaScriptAspectWeaver.java 8 Sep 2003 22:56:34 -0000 1.2
@@ -60,7 +60,10 @@
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.StringTokenizer;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.cocoon.environment.Environment;
import org.apache.excalibur.source.Source;
@@ -75,20 +78,25 @@
* <p>
* Known restrictions, to be implemented, open questions:<br/>
* <ul>
- * <li>after() interceptions are added *after* the return statement</li>
- * <li>after() interceptions have to be added in reverse order</li>
- * <li>continuations are not supported (needs another interception event
like
- * before-sendPageAndWait() and after-sendPageAndWait()</li>
- * <li>does not support object property functions</li>
- * <li>does not work for scripts loaded by "cocoon.load(...)"</li>
- * <li>throw appropriate exeptions</li>
- * <li>if applied scripts change they are not reloaded (a change in
- * the base script is necessary)</li>
- * <li>no syntax check for base script</li>
- * <li>no syntax check for scripts containing interceptions</li>
- * <li>intercepted script (result) should be pretty printed</li>
- * <li>put the result script in the directory of the base script
- * (if file protocol is used)</li>
+ * <li>after() interceptions are added *after* the "return"
+ * which is of course wrong</li>
+ * <li>after() interceptions have to be added in reverse order</li>
+ * <li>no support for object property functions</li>
+ * <li>how to deal with more than one around interception?</li>
+ * <li>does not work for scripts loaded by "cocoon.load(...)"</li>
+ * <li>if applied scripts change they are not reloaded (a change in
+ the base script is necessary)</li>
+ * <li>no syntax check for base script</li>
+ * <li>no syntax check for result scripts</li>
+ * <li>no syntax check for interception definitions</li>
+ * <li>the result script should be pretty printed
+ * and put into the directory of the base script
+ * if file protocol is used
+ * -->enables easier debugging</li>
+ * <li>pass the calling function name to the interception scripts</li>
+ * <li>review the naming of all classes and methods</li>
+ * <li>What's the purpose of the arguments in continueExecution(arguments)?
+ * See Stefano's proposal?</li>
* </ul>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Reinhard P�tz</a>
@@ -111,6 +119,9 @@
/** Does this base script contain applied scripts? */
boolean areScriptsApplied = false;
+
+ /** Provided configuration (part of the Interpreter configuration) **/
+ ArrayList stopExecutionFunctions = null;
/**
* Set the base script (the script which is scanned for applied
@@ -143,7 +154,9 @@
this.baseScriptTokenList.commentScriptsApplied();
// add the interceptions
- this.baseScriptTokenList.addInterceptions( this.interceptorGroups );
+ this.baseScriptTokenList.addInterceptions(
+ this.interceptorGroups,
+ this.stopExecutionFunctions );
// pretty print script
// TODO tbd
@@ -170,7 +183,7 @@
JSTokenList interceptorsTokensList = JSParser.parse(
readSourceIntoCharArray(
this.environement.resolveURI(source).getInputStream() ) );
- InterceptorList interceptors =
interceptorsTokensList.readInterceptionTokens();
+ InterceptionList interceptors =
interceptorsTokensList.readInterceptionTokens();
interceptors.setSourceScript( source );
for( int i = 0; i < interceptors.size(); i++ ) {
Interceptor interceptor = (Interceptor) interceptors.get( i );
@@ -188,6 +201,17 @@
this.environement = env;
}
+ /**
+ * Provide configuration (part of the Interpreter configuration)
+ */
+ public void setStopExecutionFunctionsConf( Configuration conf ) throws
ConfigurationException {
+ this.stopExecutionFunctions = new ArrayList();
+ Configuration stopExecutionFunctionsConf[] = conf.getChildren();
+ for( int i = 0; i < stopExecutionFunctionsConf.length; i++ ) {
+ stopExecutionFunctions.add(
stopExecutionFunctionsConf[i].getValue() );
+ }
+ }
+
/**
* Reset the variable containing all interceptions
*
@@ -221,7 +245,7 @@
}
/**
- * parse JavaScript and get JSTokens
+ * parse JavaScript and get a <code>JSTokenList</code>
*/
static class JSParser {
@@ -532,14 +556,24 @@
/**
* Token ids of all cocoon object occurencies followed by '.apply'
- * ( --> cocoon.apply( "bla" ); )
+ * ( cocoon.apply( "bla" ); )
*/
ArrayList cocoonPositions = new ArrayList();
/**
+ * List of all functions that lead to new continuations
+ */
+ List stopExecutionFunctions = null;
+
+
+ /**
* Add the code fragement of the passed interceptions at the right
places
*/
- private void addInterceptions(ArrayList interceptionsList) {
+ private void addInterceptions( ArrayList interceptionsList,
+ List stopExecutionFunctions ) {
+
+ this.stopExecutionFunctions = stopExecutionFunctions;
+
// add events to make parsing easier
addInterceptionEvents();
@@ -566,7 +600,7 @@
Interceptor.STR_AROUND,
li );
}
- if( type == InterceptorEvent.FNC_END ) {
+ else if( type == InterceptorEvent.FNC_END ) {
// add all after interceptors
addInterceptionTokens( interceptionsList,
ie.getName(),
@@ -574,6 +608,18 @@
li );
inAround = false;
}
+ else if( type == InterceptorEvent.CONT_EXEC ) {
+ addInterceptionTokens( interceptionsList,
+ ie.getName(),
+ Interceptor.STR_CONT_EXEC,
+ li );
+ }
+ else if( type == InterceptorEvent.STOP_EXEC ) {
+ addInterceptionTokens( interceptionsList,
+ ie.getName(),
+ Interceptor.STR_STOP_EXEC,
+ li );
+ }
}
}
}
@@ -581,7 +627,7 @@
/**
* Add all tokens in the correct order
*
- * TODO currently tokens are streamed - that's the wrong place!
+ * TODO tokens from events are streamed here --> better: add tokens
to tokensList
*
* @param interceptionsList - sorted list of all available
interceptions
* @param functionName - name of the intercepted function
@@ -590,7 +636,7 @@
* in the interceptionsList are added
* @return true if the those elements replace other JSTokens
*/
- private boolean addInterceptionTokens( ArrayList interceptionsList,
+ private boolean addInterceptionTokens( ArrayList
interceptionGroupList,
String functionName,
String eventType,
ListIterator tokensListIt
@@ -600,12 +646,12 @@
ArrayList matchingInterceptors = new ArrayList();
// read out all matching interceptions from interceptionsList
- for( int i = 0; i < interceptionsList.size(); i++ ) {
- InterceptorList interceptorList = (InterceptorList)
interceptionsList.get(i);
- ListIterator li = interceptorList.listIterator();
+ for( int i = 0; i < interceptionGroupList.size(); i++ ) {
+ InterceptionList interceptionList = (InterceptionList)
interceptionGroupList.get(i);
+ ListIterator li = interceptionList.listIterator();
while( li.hasNext() ) {
Interceptor interceptor = (Interceptor) li.next();
- interceptor.setBaseScript(
interceptorList.getSourceScript() );
+ interceptor.setBaseScript(
interceptionList.getSourceScript() );
boolean success = WildcardHelper.match(
new HashMap(), functionName + Interceptor.DELIMITER
+ eventType,
WildcardHelper.compilePattern( interceptor.getName()
));
@@ -676,7 +722,49 @@
}
/**
- * to make the interceptions easier "function start" and "function
end" events
+ * TODO current
+ * Check a token if it is function call that leads to a stop of
+ * execution (continuation is created)
+ */
+ private String isStopFunction( JSToken curToken, ListIterator
liTokens ) {
+ JSToken n1token = (JSToken) liTokens.next();
+ JSToken n2token = (JSToken) liTokens.next();
+ liTokens.previous();
+ liTokens.previous();
+
+ ListIterator li = this.stopExecutionFunctions.listIterator();
+ while( li.hasNext() ) {
+ String object = "";
+ String function = "";
+ StringTokenizer st = new StringTokenizer( (String)
li.next(), ".");
+ int countTokens = st.countTokens();
+
+ if( countTokens == 1 ) {
+ function = st.nextToken();
+ function = function.substring( 0, function.indexOf("(")
);
+ if( function.equals( curToken.toString() ) ) return
function;
+ }
+ else if( countTokens == 2 ) {
+ object = st.nextToken();
+ function = st.nextToken();
+ function = function.substring( 0, function.indexOf("(") );
+ if( object.equals( curToken.toString() ) &&
+ n1token.getType() == JSToken.POINT &&
+ function.equals( n2token.toString() ) ) {
+ return object + "." + function;
+ }
+ }
+ else {
+ // not allowed!
+ }
+
+ }
+ return null;
+ }
+
+
+ /**
+ * to make intercepting easier "function start" and "function end"
events
* are added
*/
private void addInterceptionEvents() {
@@ -691,11 +779,17 @@
String functionName = getFunctionName( pos + diff );
ListIterator li = this.listIterator( pos + diff );
boolean functionStartSet = false;
+ boolean isExecStopFunctionSet = false;
int countOpenBrackets = 0;
+ String curStopFunction = "";
+ int countCurToken = pos - 1; // get the current index
+
// continue in the token list to find opening and closing
brackets
while( li.hasNext() ) {
+ countCurToken++;
JSToken t = (JSToken) li.next();
int type = t.getType();
+ // function start is found
if( type == JSToken.BRACKET2_LEFT && ! functionStartSet
) {
li.add( new InterceptorEvent(
InterceptorEvent.FNC_START,
functionName ) );
@@ -703,7 +797,9 @@
functionStartSet = true;
continue;
}
+ // within a function
if( functionStartSet ) {
+ // end of a function is reached
if( type == JSToken.BRACKET2_RIGHT &&
countOpenBrackets == 0 ) {
li.previous();
li.add( new InterceptorEvent(
InterceptorEvent.FNC_END,
@@ -711,15 +807,43 @@
diff++;
break;
}
+ // count brackets
if( type == JSToken.BRACKET2_LEFT ) {
countOpenBrackets++;
}
else if ( type == JSToken.BRACKET2_RIGHT ) {
countOpenBrackets--;
}
+ // end of a continuation creating function reached
+ else if( isExecStopFunctionSet ) {
+ if( type == JSToken.SEMICOLON ) {
+ li.add( new InterceptorEvent(
+ InterceptorEvent.CONT_EXEC,
+ functionName ));
+ diff++;
+ isExecStopFunctionSet = false;
+ }
+ continue;
+ }
+ // check if a function is reached that creates
continuations
+ else if( type == JSToken.CODE ) {
+ String fnc = isStopFunction( t, li );
+ if( null != fnc ) {
+ curStopFunction = t.toString();
+ li.previous();
+ li.add( new InterceptorEvent(
InterceptorEvent.STOP_EXEC,
+ functionName ));
+ diff++;
+ isExecStopFunctionSet = true;
+ continue;
+ }
+ }
}
} // end while
} // end for
+
+
+
} // end method
/**
@@ -791,7 +915,7 @@
// ------------ TokenList of appield files
---------------------------
/**
- * help method to read a intercepting function name - the difference
+ * help method to read an intercepting function name - the difference
* to getFunctionName( pos ) is the wildcards "*"
*/
private String getInterceptorFunctionName( int functionPosition ) {
@@ -821,8 +945,8 @@
* <li>around(): { ... }</li>
* </ul>
*/
- private InterceptorList readInterceptionTokens() {
- InterceptorList interceptors = new InterceptorList();
+ private InterceptionList readInterceptionTokens() {
+ InterceptionList interceptors = new InterceptionList();
List functionPositions = this.getFunctionPositions();
// loop over all intercepting functions
for( int i = 0; i < functionPositions.size(); i++ ) {
@@ -847,15 +971,21 @@
// find an interception definition
if( !inInterceptor && type == JSToken.CODE ) {
- if( t.equals( "before") ) {
+ if( t.equals( Interceptor.STR_BEFORE ) ) {
interceptorType = Interceptor.STR_BEFORE;
}
- else if( t.equals( "after") ) {
+ else if( t.equals( Interceptor.STR_AFTER ) ) {
interceptorType = Interceptor.STR_AFTER;
}
- else if( t.equals( "around") ) {
+ else if( t.equals( Interceptor.STR_AROUND ) ) {
interceptorType = Interceptor.STR_AROUND;
}
+ else if( t.equals( Interceptor.STR_STOP_EXEC ))
{
+ interceptorType =
Interceptor.STR_STOP_EXEC;
+ }
+ else if( t.equals( Interceptor.STR_CONT_EXEC ))
{
+ interceptorType =
Interceptor.STR_CONT_EXEC;
+ }
}
// after interception defintion is found
else if( !inInterceptor && type ==
JSToken.BRACKET2_LEFT ) {
@@ -865,7 +995,6 @@
interceptorType );
countOpenBrackets++;
}
- // end of intercepting code is reached - clone
interception token
// and add it to the list
else if( inInterceptor && type ==
JSToken.BRACKET2_RIGHT &&
countOpenBrackets == 0 ) {
@@ -873,18 +1002,21 @@
}
// within intercepting code add all tokens
else if( inInterceptor ) {
+ // count brackets
if( type == JSToken.BRACKET2_LEFT) {
countOpenBrackets++;
}
else if( type == JSToken.BRACKET2_RIGHT ) {
countOpenBrackets--;
}
+ // end of intercepting code is reached - clone
interception token
if( type == JSToken.BRACKET2_RIGHT &&
countOpenBrackets == 0) {
interceptors.add( curInterceptor.getClone()
);
inInterceptor = false;
interceptorType = "";
curInterceptor = null;
}
+ // within intercepting code
else {
curInterceptor.addToken( t );
}
@@ -923,6 +1055,18 @@
}
return sb.toString();
}
+
+ public String debugToString() {
+ StringBuffer sb = new StringBuffer();
+ ListIterator li = this.listIterator();
+ while( li.hasNext() ) {
+ JSToken t = (JSToken) li.next();
+ sb.append("\n------------------------------------\n");
+ sb.append("type: ").append(t.getType()).append("\n");
+ sb.append( t.stream() );
+ }
+ return sb.toString();
+ }
}
@@ -930,7 +1074,7 @@
* A collection of interceptors with the possibility to
* set the source of the script where they are read from
*/
- static class InterceptorList extends ArrayList {
+ static class InterceptionList extends ArrayList {
String sourceScript;
@@ -952,6 +1096,8 @@
public static String STR_BEFORE = "before";
public static String STR_AFTER = "after";
public static String STR_AROUND = "around";
+ public static String STR_STOP_EXEC = "stopExecution";
+ public static String STR_CONT_EXEC = "continueExecution";
public static String DELIMITER = ":";
@@ -1134,7 +1280,9 @@
static class InterceptorEvent extends JSToken {
public static int FNC_START = 51;
- public static int FNC_END = 52;
+ public static int FNC_END = 52;
+ public static int STOP_EXEC = 53;
+ public static int CONT_EXEC = 54;
String interceptorName;
int type;