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;