You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2005/11/27 08:10:27 UTC

svn commit: r349187 [3/6] - in /struts/flow/trunk: ./ src/examples/WEB-INF/ src/examples/WEB-INF/guess/ src/examples/WEB-INF/portlet/ src/examples/WEB-INF/remote/ src/examples/remote/ src/java/ src/java/org/apache/struts/flow/ src/java/org/apache/strut...

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/ConversionHelper.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/ConversionHelper.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/ConversionHelper.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/ConversionHelper.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * 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.apache.struts.flow.core.javascript;
+
+import java.util.ArrayList;
+import java.util.*;
+
+import org.mozilla.javascript.Wrapper;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.JavaScriptException;
+
+/**
+ * Aids in converting from Javascript objects to Java objects and vice versa.
+ * @version $Id: LocationTrackingDebugger.java 280811 2005-09-14 09:53:32Z sylvain $
+ */
+public class ConversionHelper {
+
+    /**
+     *  Converts a JavaScript object to a HashMap
+     *
+     *@param  jsobject  The object to convert
+     *@return           The Map
+     */
+    public static Map jsobjectToMap(Scriptable jsobject) {
+        HashMap hash = new HashMap();
+        Object[] ids = jsobject.getIds();
+        for (int i = 0; i < ids.length; i++) {
+            String key = ScriptRuntime.toString(ids[i]);
+            Object value = jsobject.get(key, jsobject);
+            if (value == Undefined.instance) {
+                value = null;
+            } else {
+                value = jsobjectToObject(value);
+            }
+            hash.put(key, value);
+        }
+        return hash;
+    }
+    
+    /**
+     *  Converts a JavaScript object to a Java Object
+     *
+     *@param  obj  The JavaScript object
+     *@return      The Java Object
+     */
+    public static Object jsobjectToObject(Object obj) {
+        // unwrap Scriptable wrappers of real Java objects
+        if (obj instanceof Wrapper) {
+            obj = ((Wrapper) obj).unwrap();
+        } else if (obj == Undefined.instance) {
+            obj = null;
+        }
+        return obj;
+    }
+}
+

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/ConversionHelper.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/JSErrorReporter.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/JSErrorReporter.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/JSErrorReporter.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/JSErrorReporter.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,124 @@
+/*
+* Copyright 1999-2004 The Apache Software Foundation
+*
+* 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.apache.struts.flow.core.javascript;
+
+import org.apache.struts.flow.core.Logger;
+import org.apache.struts.flow.core.location.Location;
+import org.apache.struts.flow.core.location.LocationImpl;
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.EvaluatorException;
+import org.mozilla.javascript.tools.ToolErrorReporter;
+
+/**
+ * Implements a Rhino JavaScript {@link
+ * org.mozilla.javascript.ErrorReporter}. 
+ * Like ToolErrorReporter but logs to supplied logger instead of stdout
+ *
+ * @version CVS $Id: JSErrorReporter.java 292797 2005-09-30 16:05:46Z sylvain $
+ */
+public class JSErrorReporter implements ErrorReporter
+{
+  private Logger logger;
+  private Location location;
+  private StringBuffer message;
+
+  public JSErrorReporter(Logger logger)
+  {
+      this.logger = logger;
+  }
+  
+  private void appendMessage(String text, String sourceName, int line, int column) {
+      if (location == null) {
+          location = new LocationImpl(null, sourceName, line, column);
+          message = new StringBuffer();
+      } else {
+          // Append a linefeed
+          message.append("\n");
+      }
+      
+      message.append(text);
+  }
+
+  public void error(String message,
+                    String sourceName, int line,
+                    String lineSrc, int column)
+  {
+      String errMsg = getErrorMessage("msg.error", message, 
+                                      sourceName, line, lineSrc, column);
+      appendMessage(errMsg, sourceName, line, column);
+      System.err.println(errMsg);
+      logger.error(errMsg);
+  }
+
+  public void warning(String message, String sourceName, int line,
+                      String lineSrc, int column)
+  {
+      String errMsg = getErrorMessage("msg.warning", message, 
+                                    sourceName, line, lineSrc, column);
+      appendMessage(errMsg, sourceName, line, column);
+      System.err.println(errMsg);
+      logger.warn(errMsg);
+  }
+    
+  public EvaluatorException runtimeError(String message, String sourceName,
+                                         int line, String lineSrc,
+                                         int column)
+  {
+      String errMsg = getErrorMessage("msg.error", message,
+                                      sourceName, line,
+                                      lineSrc, column);
+      appendMessage(errMsg, sourceName, line, column);
+      System.err.println(errMsg);
+      // FIXME(SW): need to build a locatable extension to EvaluatorException
+      return new EvaluatorException(this.message.toString());
+  }
+
+  /**
+   * Formats error message
+   *
+   * @param type a <code>String</code> value, indicating the error
+   * type (error or warning)
+   * @param message a <code>String</code> value, the error or warning
+   * message
+   * @param line an <code>int</code> value, the original cummulative
+   * line number
+   * @param lineSource a <code>String</code> value, the text of the
+   * line in the file
+   * @param column an <code>int</code> value, the column in
+   * <code>lineSource</code> where the error appeared
+   * @return a <code>String</code> value, the aggregated error
+   * message, with the source file and line number adjusted to the
+   * real values
+   */
+    String getErrorMessage(String type,
+                           String message,
+                           String sourceName, int line,
+                           String lineSource, int column)
+    {
+        if (line > 0) {
+            if (sourceName != null) {
+                Object[] errArgs = { sourceName, new Integer(line), message };
+                return ToolErrorReporter.getMessage("msg.format3", errArgs);
+          } else {
+              Object[] errArgs = { new Integer(line), message };
+              return ToolErrorReporter.getMessage("msg.format2", errArgs);
+            }
+        } else {
+            Object[] errArgs = { message };
+            return ToolErrorReporter.getMessage("msg.format1", errArgs);
+        }
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/JSErrorReporter.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/LocationTrackingDebugger.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/LocationTrackingDebugger.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/LocationTrackingDebugger.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/LocationTrackingDebugger.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * 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.apache.struts.flow.core.javascript;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.struts.flow.core.FlowException;
+import org.apache.struts.flow.core.location.Location;
+import org.apache.struts.flow.core.location.LocationImpl;
+import org.apache.struts.flow.core.location.LocationUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.debug.DebugFrame;
+import org.mozilla.javascript.debug.DebuggableScript;
+import org.mozilla.javascript.debug.Debugger;
+
+/**
+ * A Rhino debugger that tracks location information when an exception is raised in some JavaScript code.
+ * It's purpose is to build a {@link org.apache.flow.core.FlowException} that holds the stacktrace
+ * in the JavaScript code.
+ * <p>
+ * This debugger implementation is designed to be as lightweight and fast as possible, in order to have a
+ * negligible impact on the performances of the Rhino interpreter.
+ * 
+ * @since 2.1.8
+ * @version $Id: LocationTrackingDebugger.java 280811 2005-09-14 09:53:32Z sylvain $
+ */
+public class LocationTrackingDebugger implements Debugger {
+    
+    // Strong reference to the location finder
+    private static final LocationUtils.LocationFinder rhinoLocFinder = new LocationUtils.LocationFinder() {
+
+        public Location getLocation(Object obj, String description) {
+            if (obj instanceof EcmaError) {
+                EcmaError ex = (EcmaError)obj;
+                if (ex.getSourceName() != null) {
+                    return new LocationImpl(ex.getName(), ex.getSourceName(), ex.getLineNumber(), ex.getColumnNumber());
+                } else {
+                    return Location.UNKNOWN;
+                }
+    
+            } else if (obj instanceof JavaScriptException) {
+                JavaScriptException ex = (JavaScriptException)obj;
+                if (ex.sourceName() != null) {
+                    return new LocationImpl(description, ex.sourceName(), ex.lineNumber(), -1);
+                } else {
+                    return Location.UNKNOWN;
+                }
+            }
+            
+            return null;
+        } 
+    };
+
+    
+    static {
+        // Register what's needed to analyze exceptions produced by Rhino
+        ExceptionUtils.addCauseMethodName("getWrappedException");
+        LocationUtils.addFinder(rhinoLocFinder);
+    }
+    
+    private List locations;
+    private Throwable throwable;
+    
+    public void handleCompilationDone(Context cx, DebuggableScript fnOrScript, String source) {
+        // nothing
+    }
+
+    public DebugFrame getFrame(Context cx, DebuggableScript fnOrScript) {
+        return new StackTrackingFrame(fnOrScript);
+    }
+
+    /**
+     * Get an exception that reflects the known location stack
+     *
+     * @param description a description for the exception
+     * @param originalException the original exception
+     * 
+     * @return a suitable exception to throw
+     * @see ProcessingException#throwLocated(String, Throwable, Location)
+     */
+    public Exception getException(String description, Exception originalException) throws FlowException {
+        if (throwable == null || locations == null) {
+            // Cannot to better for now
+            return originalException;
+        }
+
+        // Unwrap original exception, if any, wrapped by Rhino (the wrapping
+        // class is different in Rhino+cont and Rhino 1.6)
+        Throwable cause = ExceptionUtils.getCause(throwable);
+        if (cause != null)
+            throwable = cause;
+
+        return FlowException.throwLocated(description, throwable, locations);
+    }
+
+    private class StackTrackingFrame implements DebugFrame {
+        
+        DebuggableScript script;
+        int line;
+
+        public StackTrackingFrame(DebuggableScript script) {
+            this.script = script;
+        }
+        
+        public void onEnter(Context cx, Scriptable activation, Scriptable thisObj, Object[] args) {
+            // nothing
+        }
+        
+        public void onLineChange(Context cx, int lineNumber) {
+            line = lineNumber;
+        }
+
+        public void onExceptionThrown(Context cx, Throwable ex) {
+            throwable = ex;
+        }
+
+        public void onExit(Context cx, boolean byThrow, Object resultOrException) {
+            if (byThrow) {
+                String name = null;
+                if (script.isFunction()) {
+                    name = script.getFunctionName();
+                } else {
+                    name = "[script]";
+                }
+
+                if (locations == null) {
+                    locations = new ArrayList(1); // start small
+                }
+
+                locations.add(new LocationImpl(name, script.getSourceName(), line, -1));
+
+            } else if (locations != null) {
+                // The exception was handled by the script: clear any recorded locations
+                locations = null;
+            }
+        }
+    }
+}
+

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/LocationTrackingDebugger.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOMResourceNotFoundException.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOMResourceNotFoundException.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOMResourceNotFoundException.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOMResourceNotFoundException.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * 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.apache.struts.flow.core.javascript.fom;
+
+/**
+ * The exception thrown when the FOM classes can't find the necessary
+ * resource.
+ */
+public final class FOMResourceNotFoundException extends RuntimeException
+{
+    public FOMResourceNotFoundException(String message)
+    {
+        super(message);
+    }
+
+    public FOMResourceNotFoundException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOMResourceNotFoundException.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,501 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * 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.apache.struts.flow.core.javascript.fom;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.struts.flow.Constants;
+
+//import org.apache.avalon.framework.context.Context;
+//import org.apache.avalon.framework.context.ContextException;
+//import org.apache.avalon.framework.logger.Logger;
+//import org.apache.avalon.framework.service.ServiceManager;
+//import org.apache.cocoon.components.ContextHelper;
+//import org.apache.cocoon.components.LifecycleHelper;
+import org.apache.struts.flow.core.ContinuationsManager;
+import org.apache.struts.flow.core.WebContinuation;
+import org.apache.struts.flow.core.javascript.ConversionHelper;
+import org.apache.struts.flow.core.Interpreter.Argument;
+//import org.apache.cocoon.environment.ObjectModelHelper;
+//import org.apache.cocoon.environment.Redirector;
+//import org.apache.cocoon.environment.Request;
+//import org.apache.cocoon.environment.Response;
+//import org.apache.cocoon.environment.Session;
+//import org.apache.cocoon.util.ClassUtils;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.NativeJavaClass;
+import org.mozilla.javascript.NativeJavaObject;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
+import org.mozilla.javascript.continuations.Continuation;
+
+import org.apache.commons.chain.web.WebContext;
+import org.apache.struts.flow.core.Factory;
+import org.apache.struts.flow.core.Logger;
+
+/**
+ * Implementation of FOM (Flow Object Model).
+ *
+ * @since 2.1
+ * @author <a href="mailto:coliver.at.apache.org">Christopher Oliver</a>
+ * @author <a href="mailto:reinhard.at.apache.org">Reinhard P\u00F6tz</a>
+ * @version CVS $Id: FOM_Flow.java 292491 2005-09-29 17:44:16Z bloritsch $
+ */
+public class FOM_Flow extends ScriptableObject {
+
+    class CallContext {
+        CallContext caller;
+        FOM_JavaScriptInterpreter interpreter;
+        WebContext webctx;
+        Logger logger;
+        Scriptable context;
+        Scriptable parameters;
+        Scriptable log;
+        WebContinuation lastContinuation;
+        FOM_WebContinuation fwk;
+        PageLocalScopeImpl currentPageLocal;
+
+        public CallContext(CallContext caller,
+                           FOM_JavaScriptInterpreter interp,
+                           WebContext webctx,
+                           Logger logger,
+                           WebContinuation lastContinuation) {
+            this.caller = caller;
+            this.interpreter = interp;
+            this.webctx = webctx;
+            this.logger = logger;
+            this.lastContinuation = lastContinuation;
+            if (lastContinuation != null) {
+                fwk = new FOM_WebContinuation(lastContinuation);
+                Scriptable scope = FOM_Flow.this.getParentScope();
+                fwk.setParentScope(scope);
+                fwk.setPrototype(getClassPrototype(scope, fwk.getClassName()));
+                this.currentPageLocal = fwk.getPageLocal();
+            }
+            if (this.currentPageLocal != null) {
+                // "clone" the page local scope
+                this.currentPageLocal = this.currentPageLocal.duplicate();
+            } else {
+                this.currentPageLocal = new PageLocalScopeImpl(getTopLevelScope(FOM_Flow.this));
+            }
+            pageLocal.setDelegate(this.currentPageLocal);
+        }
+
+        public FOM_WebContinuation getLastContinuation() {
+            return fwk;
+        }
+
+        public void setLastContinuation(FOM_WebContinuation fwk) {
+            this.fwk = fwk;
+            if (fwk != null) {
+                pageLocal.setDelegate(fwk.getPageLocal());
+                this.lastContinuation = fwk.getWebContinuation();
+            } else {
+                this.lastContinuation = null;
+            }
+        }
+
+        public WebContext getWebContext() {
+            return webctx;
+        }
+        
+        public Scriptable getContext() {
+            if (context != null) {
+                return context;
+            }
+            context = org.mozilla.javascript.Context.toObject(webctx, getParentScope());
+            return context;
+        }
+
+        public Scriptable getLog() {
+            if (log != null) {
+                return log;
+            }
+            log = org.mozilla.javascript.Context.toObject(logger, getParentScope());
+            return log;
+        }
+
+        public Scriptable getParameters() {
+            return parameters;
+        }
+
+        public void setParameters(Scriptable parameters) {
+            this.parameters = parameters;
+        }
+    }
+
+    private CallContext currentCall;
+    protected PageLocalScopeHolder pageLocal;
+
+    public String getClassName() {
+        return "FOM_Flow";
+    }
+
+
+    // Called by FOM_JavaScriptInterpreter
+    static void init(Scriptable scope) throws Exception {
+        //FIXME(SW) what is the exact purpose of defineClass() ??
+        defineClass(scope, FOM_Flow.class);
+//        defineClass(scope, FOM_Request.class);
+//        defineClass(scope, FOM_Response.class);
+//        defineClass(scope, FOM_Cookie.class);
+//        defineClass(scope, FOM_Session.class);
+//        defineClass(scope, FOM_Context.class);
+//        defineClass(scope, FOM_Log.class);
+        defineClass(scope, FOM_WebContinuation.class);
+        defineClass(scope, PageLocalImpl.class);
+    }
+
+    void pushCallContext(FOM_JavaScriptInterpreter interp,
+                         WebContext webctx,
+                         Logger logger,
+                         WebContinuation lastContinuation) {
+        if (pageLocal == null) {
+            pageLocal = new PageLocalScopeHolder(getTopLevelScope(this));
+        }
+        
+        this.currentCall = new CallContext(currentCall, interp, webctx,
+                                           logger, lastContinuation);
+    }
+
+    void popCallContext() {
+        // Clear the scope attribute
+        //FOM_JavaScriptFlowHelper.setFOM_FlowScope(this.getObjectModel(), null);
+
+        this.currentCall = this.currentCall.caller;
+        // reset current page locals
+        if (this.currentCall != null) {
+            pageLocal.setDelegate(this.currentCall.currentPageLocal);
+        } else {
+            pageLocal.setDelegate(null);
+        }
+    }
+
+
+    public FOM_WebContinuation jsGet_continuation() {
+        // FIXME: This method can return invalid continuation! Is it OK to do so?
+        return currentCall.getLastContinuation();
+    }
+
+    public void jsSet_continuation(Object obj) {
+        FOM_WebContinuation fwk = (FOM_WebContinuation)ConversionHelper.jsobjectToObject(obj);
+        currentCall.setLastContinuation(fwk);
+    }
+
+    public FOM_WebContinuation jsFunction_forward(String uri,
+                                                   Object obj,
+                                                   Object wk)
+        throws Exception {
+            
+        WebContext ctx = currentCall.getWebContext();
+        FOM_WebContinuation fom_wk = (FOM_WebContinuation)ConversionHelper.jsobjectToObject(wk);
+        
+        if (fom_wk != null) {
+            // save page locals
+            fom_wk.setPageLocal(pageLocal.getDelegate());
+            ctx.put(Constants.CONTINUATION_ID_KEY, fom_wk.jsGet_id());
+        }
+        
+        ctx.put(Constants.FORWARD_NAME_KEY, uri);
+        ctx.put(Constants.BIZ_DATA_KEY, ConversionHelper.jsobjectToObject(obj));
+        return fom_wk;
+    }
+
+    public Scriptable jsFunction_createPageLocal() {
+        return pageLocal.createPageLocal();
+    }
+
+/*
+
+ NOTE (SM): These are the hooks to the future FOM Event Model that will be
+ designed in the future. It has been postponed because we think
+ there are more important things to do at the moment, but these
+ are left here to indicate that they are planned.
+
+    public void jsFunction_addEventListener(String eventName,
+                                            Object function) {
+        // what is this?
+    }
+
+    public void jsFunction_removeEventListener(String eventName,
+                                               Object function) {
+        // what is this?
+    }
+
+*/
+
+
+    /**
+     * Load the script file specified as argument.
+     *
+     * @param filename a <code>String</code> value
+     * @return an <code>Object</code> value
+     * @exception JavaScriptException if an error occurs
+     */
+    public Object jsFunction_load( String filename )
+        throws Exception {
+        org.mozilla.javascript.Context cx =
+            org.mozilla.javascript.Context.getCurrentContext();
+        Scriptable scope = getParentScope();
+        Script script = getInterpreter().compileScript(cx, filename);
+        return script.exec( cx, scope );
+    }
+
+
+    /**
+     * Base JS wrapper for Cocoon's request/session/context objects.
+     * <p>
+     * FIXME(SW): The only thing added to the regular Java object is the fact that
+     * attributes can be accessed as properties. Do we want to keep this?
+     */
+    private static abstract class AttributeHolderJavaObject extends NativeJavaObject {
+        
+        private static Map classProps = new HashMap();
+        private Set propNames;
+
+        public AttributeHolderJavaObject(Scriptable scope, Object object, Class clazz) {
+            super(scope, object, clazz);
+            this.propNames = getProperties(object.getClass());
+        }
+        
+        /** Compute the names of JavaBean properties so that we can filter them our in get() */
+        private static Set getProperties(Class clazz) {
+            Set result = (Set)classProps.get(clazz);
+            if (result == null) {
+                try {
+                    PropertyDescriptor[] descriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
+                    result = new HashSet();
+                    for (int i = 0; i < descriptors.length; i++) {
+                        result.add(descriptors[i].getName());
+                    }
+                } catch (IntrospectionException e) {
+                    // Cannot introspect: just consider there are no properties
+                    result = Collections.EMPTY_SET;
+                }
+                classProps.put(clazz, result);
+            }
+            return result;
+        }
+        
+        
+        protected abstract Enumeration getAttributeNames();
+        protected abstract Object getAttribute(String name);
+        
+        public Object[] getIds() {
+            // Get class Ids
+            Object [] classIds = super.getIds();
+            
+            // and add attribute names
+            ArrayList idList = new ArrayList(Arrays.asList(classIds));
+            Enumeration iter = getAttributeNames();
+            while(iter.hasMoreElements()) {
+                idList.add(iter.nextElement());
+            }
+            return idList.toArray();
+        }
+        
+        public boolean has(String name, Scriptable start) {
+            return super.has(name, start) || getAttribute(name) != null;
+        }
+        
+        public Object get(String name, Scriptable start) {
+            Object result;
+            // Filter out JavaBean properties. We only want methods of the underlying object.
+            if (this.propNames.contains(name)) {
+                result = NOT_FOUND;
+            } else {
+                result = super.get(name, start);
+            }
+            if (result == NOT_FOUND) {
+                result = getAttribute(name);
+                if (result != null) {
+                    result = org.mozilla.javascript.Context.javaToJS(result, start);
+                } else {
+                    result = NOT_FOUND;
+                }
+            }
+            return result;
+        }
+    }
+
+    
+    public Scriptable jsGet_log() {
+        return currentCall.getLog();
+    }
+
+    public Scriptable jsGet_context() {
+        return currentCall.getContext();
+    }
+
+    /**
+     * Get Sitemap parameters
+     *
+     * @return a <code>Scriptable</code> value whose properties represent
+     * the Sitemap parameters from <map:call>
+     */
+    public Scriptable jsGet_parameters() {
+        return getParameters();
+    }
+
+    public Scriptable getParameters() {
+        return currentCall.getParameters();
+    }
+
+    void setParameters(Scriptable value) {
+        currentCall.setParameters(value);
+    }
+    
+    /**
+     *  Converts a JavaScript object to a HashMap
+     */
+    public Map jsFunction_jsobjectToMap(Scriptable jsobject) {
+        return ConversionHelper.jsobjectToMap(jsobject);
+    }
+
+    // Make everything available to JavaScript objects implemented in Java:
+
+    /**
+     * Get the current context
+     * @return The context
+     */
+    public WebContext getWebContext() {
+        return currentCall.webctx;
+    }
+
+    private Logger getLogger() {
+        return currentCall.logger;
+    }
+
+    private FOM_JavaScriptInterpreter getInterpreter() {
+        return currentCall.interpreter;
+    }
+
+    /**
+     * Required by FOM_WebContinuation. This way we do not make whole Interpreter public
+     * @return interpreter Id associated with this FOM.
+     */
+    public String getInterpreterId() {
+        return getInterpreter().getInterpreterID();
+    }
+
+    /**
+     * Perform the behavior of <map:call continuation="blah">
+     * This can be used in cases where the continuation id is not encoded
+     * in the request in a form convenient to access in the sitemap.
+     * Your script can extract the id from the request and then call
+     * this method to process it as normal.
+     * @param kontId The continuation id
+     * @param parameters Any parameters you want to pass to the continuation (may be null)
+     */
+    public void handleContinuation(String kontId, Scriptable parameters)
+        throws Exception {
+        List list = null;
+        if (parameters == null || parameters == Undefined.instance) {
+            parameters = getParameters();
+        }
+        Object[] ids = parameters.getIds();
+        list = new ArrayList();
+        for (int i = 0; i < ids.length; i++) {
+            String name = ids[i].toString();
+            Argument arg = new Argument(name,
+                                        org.mozilla.javascript.Context.toString(getProperty(parameters, name)));
+            list.add(arg);
+        }
+        getInterpreter().handleContinuation(kontId, list, this.currentCall.webctx);
+    }
+
+    /**
+     * Return this continuation if it is valid, or first valid parent
+     */
+    private FOM_WebContinuation findValidParent(FOM_WebContinuation wk) {
+        if (wk != null) {
+            WebContinuation wc = wk.getWebContinuation();
+            while (wc != null && wc.disposed()) {
+                wc = wc.getParentContinuation();
+            }
+            if (wc != null) {
+                return new FOM_WebContinuation(wc);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Create a Bookmark WebContinuation from a JS Continuation with the last
+     * continuation of sendPageAndWait as its parent.
+     * PageLocal variables will be shared with the continuation of
+     * the next call to sendPageAndWait().
+     * @param k The JS continuation
+     * @param ttl Lifetime for this continuation (zero means no limit)
+     */
+    public FOM_WebContinuation jsFunction_makeWebContinuation(Object k,
+                                                              Object ttl)
+        throws Exception {
+        double d = org.mozilla.javascript.Context.toNumber(ttl);
+        FOM_WebContinuation result =
+            makeWebContinuation((Continuation)ConversionHelper.jsobjectToObject(k),
+                                findValidParent(jsGet_continuation()),
+                                (int)d);
+        result.setPageLocal(pageLocal.getDelegate());
+        currentCall.setLastContinuation(result);
+        return result;
+    }
+
+    /**
+     * Create a Web Continuation from a JS Continuation
+     * @param k The JS continuation (may be null - null will be returned in that case)
+     * @param parent The parent of this continuation (may be null)
+     * @param timeToLive Lifetime for this continuation (zero means no limit)
+     */
+    public FOM_WebContinuation makeWebContinuation(Continuation k,
+                                                   FOM_WebContinuation parent,
+                                                   int timeToLive)
+        throws Exception {
+        if (k == null) {
+            return null;
+        }
+        WebContinuation wk;
+        ContinuationsManager contMgr;
+        contMgr = Factory.getContinuationsManager();
+        wk = contMgr.createWebContinuation(ConversionHelper.jsobjectToObject(k),
+                                           (parent == null ? null : parent.getWebContinuation()),
+                                           timeToLive,
+                                           getInterpreter().getInterpreterID(),
+                                           null,
+                                           currentCall.getWebContext());
+        FOM_WebContinuation result = new FOM_WebContinuation(wk);
+        result.setParentScope(getParentScope());
+        result.setPrototype(getClassPrototype(getParentScope(),
+                                              result.getClassName()));
+        return result;
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_Flow.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,845 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * 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.apache.struts.flow.core.javascript.fom;
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.*;
+import java.util.Map;
+
+import org.apache.struts.flow.core.Logger;
+import org.apache.struts.flow.core.Factory;
+
+import org.apache.commons.chain.web.WebContext;
+import java.lang.reflect.InvocationTargetException;
+//import org.apache.avalon.framework.activity.Initializable;
+//import org.apache.avalon.framework.configuration.Configurable;
+//import org.apache.avalon.framework.configuration.Configuration;
+//import org.apache.avalon.framework.configuration.ConfigurationException;
+//mport org.apache.avalon.framework.service.ServiceManager;
+//mport org.apache.flow.ResourceNotFoundException;
+//mport org.apache.flow.components.ContextHelper;
+import org.apache.struts.flow.core.CompilingInterpreter;
+import org.apache.struts.flow.core.Interpreter;
+import org.apache.struts.flow.core.InvalidContinuationException;
+import org.apache.struts.flow.core.*;
+import org.apache.struts.flow.core.javascript.JSErrorReporter;
+import org.apache.struts.flow.core.javascript.LocationTrackingDebugger;
+//import org.apache.struts.flow.core.javascript.ScriptablePointerFactory;
+//import org.apache.struts.flow.core.javascript.ScriptablePropertyHandler;
+//import org.apache.flow.environment.ObjectModelHelper;
+//import org.apache.flow.environment.Redirector;
+//import org.apache.flow.environment.Request;
+//import org.apache.flow.environment.Session;
+//import org.apache.commons.jxpath.JXPathIntrospector;
+//import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
+import org.apache.struts.flow.core.source.Source;
+//import org.apache.regexp.RE;
+//import org.apache.regexp.RECompiler;
+//import org.apache.regexp.REProgram;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.NativeJavaClass;
+import org.mozilla.javascript.NativeJavaPackage;
+import org.mozilla.javascript.PropertyException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.WrappedException;
+import org.mozilla.javascript.Wrapper;
+import org.mozilla.javascript.WrapFactory;
+import org.mozilla.javascript.continuations.Continuation;
+import org.mozilla.javascript.tools.debugger.Main;
+import org.mozilla.javascript.tools.shell.Global;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * Interface with the JavaScript interpreter.
+ *
+ * @author <a href="mailto:ovidiu@apache.org">Ovidiu Predescu</a>
+ * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
+ * @since March 25, 2002
+ * @version CVS $Id: FOM_JavaScriptInterpreter.java 307410 2005-10-09 12:17:33Z reinhard $
+ */
+public class FOM_JavaScriptInterpreter extends CompilingInterpreter {
+
+    /**
+     * A long value is stored under this key in each top level JavaScript
+     * thread scope object. When you enter a context any scripts whose
+     * modification time is later than this value will be recompiled and reexecuted,
+     * and this value will be updated to the current time.
+     */
+    private final static String LAST_EXEC_TIME = "__PRIVATE_LAST_EXEC_TIME__";
+
+    /**
+     * Prefix for session/request attribute storing JavaScript global scope object.
+     */
+    private static final String USER_GLOBAL_SCOPE = "FOM JavaScript GLOBAL SCOPE/";
+
+    /**
+     * This is the only optimization level that supports continuations
+     * in the Christoper Oliver's Rhino JavaScript implementation
+     */
+    private static final int OPTIMIZATION_LEVEL = -2;
+
+    /**
+     * When was the last time we checked for script modifications. Used
+     * only if {@link #reloadScripts} is true.
+     */
+    private long lastTimeCheck;
+
+    /**
+     * Shared global scope for scripts and other immutable objects
+     */
+    private Global scope;
+
+    /**
+     * List of <code>String</code> objects that represent files to be
+     * read in by the JavaScript interpreter.
+     */
+    private List topLevelScripts = new ArrayList();
+
+    private boolean enableDebugger;
+    
+    private WrapFactory wrapFactory;
+    private List registrars = new ArrayList();
+
+    /**
+     * JavaScript debugger: there's only one of these: it can debug multiple
+     * threads executing JS code.
+     */
+    private static Main debugger;
+
+    static synchronized Main getDebugger() {
+        if (debugger == null) {
+            final Main db = new Main("Flow Debugger");
+            db.pack();
+            Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
+            size.width *= 0.75;
+            size.height *= 0.75;
+            db.setSize(size);
+            db.setExitAction(new Runnable() {
+                    public void run() {
+                        db.setVisible(false);
+                    }
+                });
+            db.setOptimizationLevel(OPTIMIZATION_LEVEL);
+            db.setVisible(true);
+            debugger = db;
+            Context.addContextListener(debugger);
+        }
+        return debugger;
+    }
+    
+    /**
+     *  Gets the logger attribute of the JavaScriptInterpreter object
+     *
+     *@return    The logger value
+     */
+    public Logger getLogger() {
+        return Factory.getLogger();
+    }
+    
+    /**
+     *  Sets the wrap factory to use with Rhino
+     *
+     *@param wf The WrapFactory instance
+     */
+    public void setWrapFactory(WrapFactory wf) {
+        this.wrapFactory = wf;
+    }
+    
+    /**
+     *  Adds a class that will register a global variable
+     *
+     * @param reg The variable registrar
+     */
+    public void addVariableRegistrar(VariableRegistrar reg) {
+        registrars.add(reg);
+    }
+
+    /**
+     *  Removes a class that will register a global variable
+     *
+     * @param reg The variable registrar
+     */
+    public void removeVariableRegistrar(VariableRegistrar reg) {
+        registrars.remove(reg);
+    } 
+    
+
+    /**
+     *  Sets the interval between when the script should be looked at to see if
+     *  it needs to be reloaded
+     *
+     *@param  time  The interval time in milliseconds
+     */
+    public void setCheckTime(long time) {
+        checkTime = time;
+    }
+    
+    
+    /**
+     *  Sets whether to enable the debugger
+     *
+     *@param  val  The new debugger value
+     */
+    public void setDebugger(boolean val) {
+        enableDebugger = val;
+    }
+
+
+    /**
+     *  Sets whether to try to reload modified scripts or not
+     *
+     *@param  val  True to reload
+     */
+    public void setReloadScripts(boolean val) {
+        reloadScripts = val;
+    }
+
+    /**
+     *  Initialize the global scope
+     *
+     *@exception  FlowException  If anything goes wrong
+     */
+    public void initialize() throws FlowException {
+        initialize(null);
+    }
+    
+
+    /**
+     *  Initialize the global scope
+     *
+     *@exception  FlowException  If anything goes wrong
+     */
+    public void initialize(List classes) throws FlowException {
+        if (enableDebugger) {
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Flow debugger enabled, creating");
+            }
+            getDebugger().doBreak();
+        }
+        Context context = Context.enter();
+        context.setOptimizationLevel(OPTIMIZATION_LEVEL);
+        context.setCompileFunctionsWithDynamicScope(true);
+        context.setGeneratingDebug(true);
+        if (wrapFactory != null) {
+            context.setWrapFactory(wrapFactory);
+        }
+
+        try {
+            scope = new Global(context);
+            
+            // Register some handy classes with JavaScript, so we can make
+            // use of them from the flow layer.
+            initScope(context, scope);
+
+            // Register any custom classes
+            if (classes != null) {
+                for (Iterator i = classes.iterator(); i.hasNext(); ) {
+                    ScriptableObject.defineClass(scope, (Class)i.next());
+                }
+            }
+            
+            // Access to Cocoon internal objects
+            FOM_Flow.init(scope);
+        } catch (Exception e) {
+            Context.exit();
+            e.printStackTrace();
+            throw new FlowException(e);
+        }
+    }
+    
+    
+    /**
+     *  Initialize the global scope
+     *
+     *@param  context                        The context
+     *@param  scope                          The scope to initialize
+     *@exception  IllegalAccessException     If anything goes wrong
+     *@exception  InstantiationException     If anything goes wrong
+     *@exception  InvocationTargetException  If anything goes wrong
+     *@exception  JavaScriptException        If anything goes wrong
+     */
+    protected void initScope(Context context, Global scope) throws IllegalAccessException,
+            InstantiationException, InvocationTargetException, JavaScriptException {
+        
+        VariableRegistrar reg;
+        List lst;
+        for (Iterator i = registrars.iterator(); i.hasNext(); ) {
+            reg = (VariableRegistrar) i.next();
+            ScriptableObject.defineClass(scope, reg.getClassInstance());
+        }
+
+        // Define some global variables in JavaScript
+        Object args[] = {};
+
+        for (Iterator i = registrars.iterator(); i.hasNext(); ) {
+            reg = (VariableRegistrar) i.next();
+            if (!reg.isCallSpecific()) {
+                Scriptable var = context.newObject(scope, reg.getClassName(), args);
+                scope.put(reg.getName(), scope, reg.getInstance(scope, null));
+            }    
+        }
+
+
+    }
+
+
+    /**
+     * Returns the JavaScript scope, a Scriptable object, from the user
+     * session instance. Each interpreter instance can have a scope
+     * associated with it.
+     *
+     * @return a <code>ThreadScope</code> value
+     */
+    private ThreadScope getSessionScope(WebContext ctx) throws Exception {
+        final String scopeID = USER_GLOBAL_SCOPE + getInterpreterID();
+
+        ThreadScope scope = null;
+
+        // Get/create the scope attached to the current context
+        //Session session = request.getSession(false);
+        //if (session != null) {
+        //    scope = (ThreadScope) session.getAttribute(scopeID);
+        //} else {
+        //    scope = (ThreadScope) request.getAttribute(scopeID);
+        //}
+        
+        // FIXME: This might create a unwanted session
+        scope = (ThreadScope) ctx.getSessionScope().get(scopeID);
+        if (scope == null) {
+            scope = (ThreadScope) ctx.getRequestScope().get(scopeID);
+        }
+        
+
+        if (scope == null) {
+            scope = createThreadScope();
+            // Save scope in the request early to allow recursive Flow calls
+            ctx.getRequestScope().put(scopeID, scope);
+        }
+
+        return scope;
+    }
+
+    /**
+     * Associates a JavaScript scope, a Scriptable object, with
+     * {@link #getInterpreterID() identifier} of this {@link Interpreter}
+     * instance.
+     *
+     * @param scope a <code>ThreadScope</code> value
+     */
+    private void setSessionScope(ThreadScope scope, WebContext ctx) throws Exception {
+        if (scope.useSession) {
+            final String scopeID = USER_GLOBAL_SCOPE + getInterpreterID();
+
+            // FIXME: Where "session scope" should go when session is invalidated?
+            // Attach the scope to the current context
+            try {
+                ctx.getSessionScope().put(scopeID, scope);
+            } catch (IllegalStateException e) {
+                // Session might be invalidated already.
+                if (getLogger().isDebugEnabled()) {
+                    getLogger().debug("Got '" + e + "' while trying to set session scope.", e);
+                }
+            }
+        }
+    }
+
+    public static class ThreadScope extends ScriptableObject {
+        private static final String[] BUILTIN_PACKAGES = {"javax", "org", "com"};
+
+        private ClassLoader classLoader;
+
+        /* true if this scope has assigned any global vars */
+        boolean useSession;
+
+        boolean locked = false;
+
+        /**
+         * Initializes new top-level scope.
+         */
+        public ThreadScope(Global scope) throws Exception {
+            final Context context = Context.getCurrentContext();
+
+            final String[] names = { "importClass" };
+            try {
+                defineFunctionProperties(names,
+                                         ThreadScope.class,
+                                         ScriptableObject.DONTENUM);
+            } catch (PropertyException e) {
+                throw new Error();  // should never happen
+            }
+
+            setPrototype(scope);
+
+            // We want this to be a new top-level scope, so set its
+            // parent scope to null. This means that any variables created
+            // by assignments will be properties of this.
+            setParentScope(null);
+
+            // Put in the thread scope the Cocoon object, which gives access
+            // to the interpreter object, and some Cocoon objects. See
+            // FOM_Flow for more details.
+            final Object[] args = {};
+            FOM_Flow flow = (FOM_Flow) context.newObject(this,
+                                                               "FOM_Flow",
+                                                               args);
+            flow.setParentScope(this);
+            super.put("flow", this, flow);
+
+            defineProperty(LAST_EXEC_TIME,
+                           new Long(0),
+                           ScriptableObject.DONTENUM | ScriptableObject.PERMANENT);
+        }
+
+        public String getClassName() {
+            return "ThreadScope";
+        }
+
+        public void setLock(boolean lock) {
+            this.locked = lock;
+        }
+
+        public void put(String name, Scriptable start, Object value) {
+            //Allow setting values to existing variables, or if this is a
+            //java class (used by importClass & importPackage)
+            if (this.locked && !has(name, start) && !(value instanceof NativeJavaClass)) {
+                // Need to wrap into a runtime exception as Scriptable.put has no throws clause...
+                throw new WrappedException (new JavaScriptException("Implicit declaration of global variable '" + name +
+                  "' forbidden. Please ensure all variables are explicitely declared with the 'var' keyword"));
+            }
+            this.useSession = true;
+            super.put(name, start, value);
+        }
+
+        public void put(int index, Scriptable start, Object value) {
+            // FIXME(SW): do indexed properties have a meaning on the global scope?
+            if (this.locked && !has(index, start)) {
+                throw new WrappedException(new JavaScriptException("Global scope locked. Cannot set value for index " + index));
+            }
+            this.useSession = true;
+            super.put(index, start, value);
+        }
+
+        // Invoked after script execution
+        void onExec() {
+            this.useSession = false;
+            super.put(LAST_EXEC_TIME, this, new Long(System.currentTimeMillis()));
+        }
+
+        /** Override importClass to allow reloading of classes */
+        public static void importClass(Context ctx,
+                                       Scriptable thisObj,
+                                       Object[] args,
+                                       Function funObj) {
+            for (int i = 0; i < args.length; i++) {
+                Object clazz = args[i];
+                if (!(clazz instanceof NativeJavaClass)) {
+                    throw Context.reportRuntimeError("Not a Java class: " +
+                                                     Context.toString(clazz));
+                }
+                String s = ((NativeJavaClass) clazz).getClassObject().getName();
+                String n = s.substring(s.lastIndexOf('.') + 1);
+                thisObj.put(n, thisObj, clazz);
+            }
+        }
+
+        public void setupPackages(ClassLoader cl) throws Exception {
+            final String JAVA_PACKAGE = "JavaPackage";
+            if (classLoader != cl) {
+                classLoader = cl;
+                Scriptable newPackages = new NativeJavaPackage("", cl);
+                newPackages.setParentScope(this);
+                newPackages.setPrototype(ScriptableObject.getClassPrototype(this, JAVA_PACKAGE));
+                super.put("Packages", this, newPackages);
+                for (int i = 0; i < BUILTIN_PACKAGES.length; i++) {
+                    String pkgName = BUILTIN_PACKAGES[i];
+                    Scriptable pkg = new NativeJavaPackage(pkgName, cl);
+                    pkg.setParentScope(this);
+                    pkg.setPrototype(ScriptableObject.getClassPrototype(this, JAVA_PACKAGE));
+                    super.put(pkgName, this, pkg);
+                }
+            }
+        }
+
+        public ClassLoader getClassLoader() {
+            return classLoader;
+        }
+    }
+
+    private ThreadScope createThreadScope() throws Exception {
+        return new ThreadScope(scope);
+    }
+
+    /**
+     * Returns a new Scriptable object to be used as the global scope
+     * when running the JavaScript scripts in the context of a request.
+     *
+     * <p>If you want to maintain the state of global variables across
+     * multiple invocations of <code>&lt;map:call
+     * function="..."&gt;</code>, you need to instanciate the session
+     * object which is a property of the flow object
+     * <code>var session = flow.session</code>. This will place the
+     * newly create Scriptable object in the user's session, where it
+     * will be retrieved from at the next invocation of {@link #callFunction}.</p>
+     *
+     * @exception Exception if an error occurs
+     */
+    private void setupContext(WebContext webctx, Context context,
+                              ThreadScope thrScope)
+    throws Exception {
+        // Try to retrieve the scope object from the session instance. If
+        // no scope is found, we create a new one, but don't place it in
+        // the session.
+        //
+        // When a user script "creates" a session using
+        // flow.createSession() in JavaScript, the thrScope is placed in
+        // the session object, where it's later retrieved from here. This
+        // behaviour allows multiple JavaScript functions to share the
+        // same global scope.
+
+        FOM_Flow flow = (FOM_Flow) thrScope.get("flow", thrScope);
+        long lastExecTime = ((Long) thrScope.get(LAST_EXEC_TIME,
+                                                 thrScope)).longValue();
+        boolean needsRefresh = false;
+        if (reloadScripts) {
+            long now = System.currentTimeMillis();
+            if (now >= lastTimeCheck + checkTime) {
+                needsRefresh = true;
+            }
+            lastTimeCheck = now;
+        }
+        
+        // We need to setup the FOM_Flow object according to the current
+        // request. Everything else remains the same.
+        ClassLoader contextClassloader = Thread.currentThread().getContextClassLoader();
+        thrScope.setupPackages(contextClassloader);
+        flow.pushCallContext(this, webctx, getLogger(), null);
+        
+        VariableRegistrar reg;
+        for (Iterator i = registrars.iterator(); i.hasNext(); ) {
+            reg = (VariableRegistrar) i.next();
+            if (reg.isCallSpecific()) {
+                WrapFactory factory = context.getWrapFactory();
+                Object o = reg.getInstance(thrScope, webctx);
+                if (o instanceof Scriptable) {
+                    thrScope.put(reg.getName(), thrScope, (Scriptable)o);
+                } else {
+                    Scriptable var = factory.wrapAsJavaObject(context, thrScope, o, reg.getClassInstance());
+                    thrScope.put(reg.getName(), thrScope, var);
+                }
+            }    
+        }
+
+        // Check if we need to compile and/or execute scripts
+        synchronized (compiledScripts) {
+            List execList = new ArrayList();
+            // If we've never executed scripts in this scope or
+            // if reload-scripts is true and the check interval has expired
+            // or if new scripts have been specified in the sitemap,
+            // then create a list of scripts to compile/execute
+            if (lastExecTime == 0 || needsRefresh || needResolve.size() > 0) {
+                topLevelScripts.addAll(needResolve);
+                if (lastExecTime != 0 && !needsRefresh) {
+                    execList.addAll(needResolve);
+                } else {
+                    execList.addAll(topLevelScripts);
+                }
+                needResolve.clear();
+            }
+            // Compile all the scripts first. That way you can set breakpoints
+            // in the debugger before they execute.
+            for (int i = 0, size = execList.size(); i < size; i++) {
+                String sourceURI = (String)execList.get(i);
+                ScriptSourceEntry entry =
+                    (ScriptSourceEntry)compiledScripts.get(sourceURI);
+                if (entry == null) {
+                    Source src = this.sourceresolver.resolveURI(sourceURI);
+                    entry = new ScriptSourceEntry(src);
+                    compiledScripts.put(sourceURI, entry);
+                }
+                // Compile the script if necessary
+                entry.getScript(context, this.scope, needsRefresh, this);
+            }
+            // Execute the scripts if necessary
+            for (int i = 0, size = execList.size(); i < size; i++) {
+                String sourceURI = (String) execList.get(i);
+                ScriptSourceEntry entry =
+                    (ScriptSourceEntry) compiledScripts.get(sourceURI);
+                long lastMod = entry.getSource().getLastModified();
+                Script script = entry.getScript(context, this.scope, false, this);
+                if (lastExecTime == 0 || lastMod > lastExecTime) {
+                    script.exec(context, thrScope);
+                    thrScope.onExec();
+                }
+            }
+        }
+    }
+
+    /**
+     * Compile filename as JavaScript code
+     *
+     * @param cx Rhino context
+     * @param fileName resource uri
+     * @return compiled script
+     */
+    Script compileScript(Context cx, String fileName) throws Exception {
+        Source src = this.sourceresolver.resolveURI(fileName);
+        if (src != null) {
+            synchronized (compiledScripts) {
+                ScriptSourceEntry entry =
+                    (ScriptSourceEntry)compiledScripts.get(src.getURI());
+                Script compiledScript = null;
+                if (entry == null) {
+                    compiledScripts.put(src.getURI(),
+                            entry = new ScriptSourceEntry(src));
+                } else {
+                    this.sourceresolver.release(src);
+                }
+                compiledScript = entry.getScript(cx, this.scope, false, this);
+                return compiledScript;
+            }
+        }
+        throw new FlowException(fileName + ": not found");
+
+    }
+
+    protected Script compileScript(Context cx, Scriptable scope, Source src)
+    throws Exception {
+        PushbackInputStream is = new PushbackInputStream(src.getInputStream(), ENCODING_BUF_SIZE);
+        try {
+            String encoding = findEncoding(is);
+            Reader reader = encoding == null ? new InputStreamReader(is) : new InputStreamReader(is, encoding);
+            reader = new BufferedReader(reader);
+            Script compiledScript = cx.compileReader(scope, reader,
+                    src.getURI(), 1, null);
+            return compiledScript;
+        } finally {
+            is.close();
+        }
+    }
+    
+    // A charset name can be up to 40 characters taken from the printable characters of US-ASCII
+    // (see http://www.iana.org/assignments/character-sets). So reading 100 bytes should be more than enough.
+    private final static int ENCODING_BUF_SIZE = 100;
+    // Match 'encoding = xxxx' on the first line
+    Pattern encodingRE = Pattern.compile("^.*encoding\\s*=\\s*([^\\s]*)");
+    
+    /**
+     * Find the encoding of the stream, or null if not specified
+     */
+    String findEncoding(PushbackInputStream is) throws IOException {
+        // Read some bytes
+        byte[] buffer = new byte[ENCODING_BUF_SIZE];
+        int len = is.read(buffer, 0, buffer.length);
+        // and push them back
+        is.unread(buffer, 0, len);
+        
+        // Interpret them as an ASCII string
+        String str = new String(buffer, 0, len, "ASCII");
+        Matcher re = encodingRE.matcher(str);
+        if (re.matches()) {
+            return re.group(1);
+        }
+        return null;
+    }
+
+    /**
+     * Calls a JavaScript function, passing <code>params</code> as its
+     * arguments. In addition to this, it makes available the parameters
+     * through the <code>flow.parameters</code> JavaScript array
+     * (indexed by the parameter names).
+     *
+     * @param funName a <code>String</code> value
+     * @param params a <code>List</code> value
+     * @param redirector
+     * @exception Exception if an error occurs
+     */
+    public Object callFunction(String funName, List params, WebContext webctx)
+    throws Exception {
+        Context context = Context.enter();
+        context.setOptimizationLevel(OPTIMIZATION_LEVEL);
+        context.setGeneratingDebug(true);
+        context.setCompileFunctionsWithDynamicScope(true);
+        context.setErrorReporter(new JSErrorReporter(getLogger()));
+        
+        LocationTrackingDebugger locationTracker = new LocationTrackingDebugger();
+        if (!enableDebugger) {
+            //FIXME: add a "tee" debugger that allows both to be used simultaneously
+            context.setDebugger(locationTracker, null);
+        }
+        Object ret = null;
+        ThreadScope thrScope = getSessionScope(webctx);
+        synchronized (thrScope) {
+            ClassLoader savedClassLoader =
+                Thread.currentThread().getContextClassLoader();
+            FOM_Flow flow = null;
+            try {
+                try {
+                    setupContext(webctx, context, thrScope);
+                    flow = (FOM_Flow) thrScope.get("flow", thrScope);
+
+                    // Register the current scope for scripts indirectly called from this function
+                    //FOM_JavaScriptFlowHelper.setFOM_FlowScope(flow.getObjectModel(), thrScope);
+
+                    if (enableDebugger) {
+                        if (!getDebugger().isVisible()) {
+                            // only raise the debugger window if it isn't already visible
+                            getDebugger().setVisible(true);
+                        }
+                    }
+
+                    int size = (params != null ? params.size() : 0);
+                    Scriptable parameters = context.newObject(thrScope);
+                    for (int i = 0; i < size; i++) {
+                        Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
+                        if (arg.name == null) {
+                            arg.name = "";
+                        }
+                        parameters.put(arg.name, parameters, arg.value);
+                    }
+                    flow.setParameters(parameters);
+
+                    Object fun = ScriptableObject.getProperty(thrScope, funName);
+                    if (fun == Scriptable.NOT_FOUND) {
+                        throw new FlowException("Function \"javascript:" + funName + "()\" not found");
+                    }
+
+                    thrScope.setLock(true);
+                    ret = ScriptRuntime.call(context, fun, thrScope, new Object[0], thrScope);
+                } catch (JavaScriptException ex) {
+                    throw locationTracker.getException("Error calling flowscript function " + funName, ex);
+                } catch (EcmaError ee) {
+                    throw locationTracker.getException("Error calling function " + funName, ee);
+                } catch (WrappedException ee) {
+                    throw locationTracker.getException("Error calling function " + funName, ee);
+                }
+            } finally {
+                thrScope.setLock(false);
+                setSessionScope(thrScope, webctx);
+                if (flow != null) {
+                    flow.popCallContext();
+                }
+                Context.exit();
+                Thread.currentThread().setContextClassLoader(savedClassLoader);
+            }
+        }
+        return ret;
+    }
+
+    public void handleContinuation(String id, List params,
+                                   WebContext webctx) throws Exception
+    {
+        WebContinuation wk = continuationsMgr.lookupWebContinuation(id, getInterpreterID(), webctx);
+
+        if (wk == null) {
+            /*
+             * Throw an InvalidContinuationException to be handled inside the
+             * <map:handle-errors> sitemap element.
+             */
+            throw new InvalidContinuationException("The continuation ID " + id + " is invalid.");
+        }
+
+        Context context = Context.enter();
+        context.setOptimizationLevel(OPTIMIZATION_LEVEL);
+        context.setGeneratingDebug(true);
+        context.setCompileFunctionsWithDynamicScope(true);
+        LocationTrackingDebugger locationTracker = new LocationTrackingDebugger();
+        if (!enableDebugger) {
+            //FIXME: add a "tee" debugger that allows both to be used simultaneously
+            context.setDebugger(locationTracker, null);
+        }
+
+        // Obtain the continuation object from it, and setup the
+        // FOM_Flow object associated in the dynamic scope of the saved
+        // continuation with the environment and context objects.
+        Continuation k = (Continuation)wk.getContinuation();
+        ThreadScope kScope = (ThreadScope)k.getParentScope();
+        synchronized (kScope) {
+            ClassLoader savedClassLoader =
+                Thread.currentThread().getContextClassLoader();
+            FOM_Flow flow = null;
+            try {
+                Thread.currentThread().setContextClassLoader(kScope.getClassLoader());
+                flow = (FOM_Flow)kScope.get("flow", kScope);
+                kScope.setLock(true);
+                flow.pushCallContext(this, webctx, getLogger(), wk);
+
+                // Register the current scope for scripts indirectly called from this function
+                //FOM_JavaScriptFlowHelper.setFOM_FlowScope(flow.getObjectModel(), kScope);
+
+                if (enableDebugger) {
+                    getDebugger().setVisible(true);
+                }
+                Scriptable parameters = context.newObject(kScope);
+                int size = params != null ? params.size() : 0;
+                for (int i = 0; i < size; i++) {
+                    Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
+                    parameters.put(arg.name, parameters, arg.value);
+                }
+                flow.setParameters(parameters);
+                FOM_WebContinuation fom_wk = new FOM_WebContinuation(wk);
+                fom_wk.setParentScope(kScope);
+                fom_wk.setPrototype(ScriptableObject.getClassPrototype(kScope,
+                                                                       fom_wk.getClassName()));
+                Object[] args = new Object[] {k, fom_wk};
+                try {
+                    ScriptableObject.callMethod(flow,
+                                                "handleContinuation", args);
+                } catch (JavaScriptException ex) {
+                    throw locationTracker.getException("Error calling continuation", ex);
+
+                } catch (EcmaError ee) {
+                    throw locationTracker.getException("Error calling continuation", ee);
+
+                }
+            } finally {
+                kScope.setLock(false);
+                setSessionScope(kScope, webctx);
+                if (flow != null) {
+                    flow.popCallContext();
+                }
+                Context.exit();
+                Thread.currentThread().setContextClassLoader(savedClassLoader);
+            }
+        }
+    }
+
+    private Throwable unwrap(JavaScriptException e) {
+        Object value = e.getValue();
+        while (value instanceof Wrapper) {
+            value = ((Wrapper)value).unwrap();
+        }
+        if (value instanceof Throwable) {
+            return (Throwable)value;
+        }
+        return e;
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_JavaScriptInterpreter.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_WebContinuation.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_WebContinuation.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_WebContinuation.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_WebContinuation.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,254 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * 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.apache.struts.flow.core.javascript.fom;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.struts.flow.core.Factory;
+
+import org.apache.struts.flow.core.ContinuationsManager;
+import org.apache.struts.flow.core.WebContinuation;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
+import org.mozilla.javascript.continuations.Continuation;
+
+/**
+ *
+ * @version CVS $Id: FOM_WebContinuation.java 292158 2005-09-28 10:24:51Z sylvain $
+ */
+public class FOM_WebContinuation extends ScriptableObject {
+
+    WebContinuation wk;
+
+
+    static class UserObject {
+        boolean isBookmark;
+        PageLocalScopeImpl pageLocal;
+    }
+
+    static private boolean isBookmark(WebContinuation wk) {
+        UserObject userObj = (UserObject)wk.getUserObject();
+        if (userObj == null) {
+            return false;
+        }
+        return userObj.isBookmark;
+    }
+
+    public FOM_WebContinuation() {
+        this(null);
+    }
+
+
+    public FOM_WebContinuation(WebContinuation wk) {
+        this.wk = wk;
+    }
+
+    // new FOM_WebContinuation([Continuation] continuation,
+    //                         [FOM_WebContinuation] parent,
+    //                         [Number] timeToLive)
+    public static Object jsConstructor(Context cx, Object[] args,
+                                       Function ctorObj,
+                                       boolean inNewExpr)
+        throws Exception {
+        FOM_WebContinuation result = null;
+        if (args.length < 1) {
+            // error
+        }
+        Continuation c = (Continuation)unwrap(args[0]);
+        FOM_WebContinuation parent = null;
+        if (args.length > 1) {
+            parent = (FOM_WebContinuation)args[1];
+        }
+        int timeToLive = 0;
+        if (args.length > 2) {
+            timeToLive =
+                (int)org.mozilla.javascript.Context.toNumber(args[2]);
+        }
+        WebContinuation wk;
+        Scriptable scope = getTopLevelScope(c);
+        FOM_Flow cocoon = (FOM_Flow)getProperty(scope, "flow");
+        ContinuationsManager contMgr = Factory.getContinuationsManager();
+        wk = contMgr.createWebContinuation(c,
+                                           (parent == null ? null : parent.getWebContinuation()),
+                                           timeToLive,
+                                           cocoon.getInterpreterId(), 
+                                           null,
+                                           cocoon.getWebContext());
+        result = new FOM_WebContinuation(wk);
+        result.setParentScope(getTopLevelScope(scope));
+        result.setPrototype(getClassPrototype(scope, result.getClassName()));
+        return result;
+    }
+
+    public String getClassName() {
+        return "FOM_WebContinuation";
+    }
+
+    public Object jsFunction_getAttribute(String name) {
+        return org.mozilla.javascript.Context.javaToJS(
+                wk.getAttribute(name),
+                getParentScope());
+    }
+
+    public void jsFunction_setAttribute(String name, Object value) {
+        wk.setAttribute(name, unwrap(value));
+    }
+
+    public void jsFunction_removeAttribute(String name) {
+        wk.removeAttribute(name);
+    }
+
+    public Object jsFunction_getAttributeNames() {
+        return org.mozilla.javascript.Context.javaToJS(
+                wk.getAttributeNames(),
+                getParentScope());
+    }
+
+    public String jsGet_id() {
+        return wk.getId();
+    }
+
+
+    public Continuation jsGet_continuation() {
+        return (Continuation)wk.getContinuation();
+    }
+
+    public FOM_WebContinuation jsFunction_getParent() {
+        WebContinuation parent = wk.getParentContinuation();
+        if (parent == null) {
+            return null;
+        }
+
+        FOM_WebContinuation pwk = new FOM_WebContinuation(parent);
+        pwk.setParentScope(getParentScope());
+        pwk.setPrototype(getClassPrototype(getParentScope(),
+                                           pwk.getClassName()));
+        return pwk;
+    }
+
+    public NativeArray jsFunction_getChildren() throws Exception {
+        List list = wk.getChildren();
+        NativeArray arr =
+            (NativeArray)org.mozilla.javascript.Context.getCurrentContext().newObject(getParentScope(),
+                                                                                      "Array",
+                                                                                      new Object[]{new Integer(list.size())});
+        Iterator iter = list.iterator();
+        for (int i = 0; iter.hasNext(); i++) {
+            WebContinuation child = (WebContinuation)iter.next();
+            FOM_WebContinuation cwk = new FOM_WebContinuation(child);
+            cwk.setParentScope(getParentScope());
+            cwk.setPrototype(getClassPrototype(getParentScope(),
+                                               cwk.getClassName()));
+            arr.put(i, arr, cwk);
+        }
+        return arr;
+    }
+
+    public void jsFunction_invalidate() throws Exception {
+        ContinuationsManager contMgr = null;
+        FOM_Flow cocoon =
+            (FOM_Flow)getProperty(getTopLevelScope(this), "flow");
+        contMgr = Factory.getContinuationsManager();
+        contMgr.invalidateWebContinuation(wk, cocoon.getWebContext());
+    }
+
+    public void jsFunction_display() {
+        wk.display();
+    }
+
+    public WebContinuation getWebContinuation() {
+        return wk;
+    }
+
+    private static Object unwrap(Object obj) {
+        if (obj instanceof Wrapper) {
+            obj = ((Wrapper)obj).unwrap();
+        } else if (obj == Undefined.instance) {
+            obj = null;
+        }
+        return obj;
+    }
+
+    PageLocalScopeImpl getPageLocal() {
+        UserObject userObj = (UserObject)wk.getUserObject();
+        if (userObj == null) return null;
+        return userObj.pageLocal;
+    }
+
+    void setPageLocal(PageLocalScopeImpl pageLocal) {
+        UserObject userObj = (UserObject)wk.getUserObject();
+        if (userObj == null) {
+            userObj = new UserObject();
+            wk.setUserObject(userObj);
+        }
+        userObj.pageLocal = pageLocal;
+    }
+
+    public void jsFunction_setBookmark(boolean value) {
+        UserObject userObj = (UserObject)wk.getUserObject();
+        if (userObj == null) {
+            userObj = new UserObject();
+            wk.setUserObject(userObj);
+        }
+        userObj.isBookmark = value;
+    }
+
+    public boolean jsGet_bookmark() {
+        return isBookmark(wk);
+    }
+
+    public boolean jsFunction_isBookmark() {
+        return isBookmark(wk);
+    }
+
+    public FOM_WebContinuation jsGet_previousBookmark() {
+        WebContinuation c = wk.getParentContinuation();
+        if (c == null) {
+            return null;
+        }
+
+        // If this is a continuation of sendPageAndWait()
+        // and the immediate parent is a bookmark, then
+        // it is the bookmark for this page, so skip it.
+        if (!isBookmark(wk) && isBookmark(c)) {
+            c = c.getParentContinuation();
+        }
+        while (c != null && !isBookmark(c)) {
+            c = c.getParentContinuation();
+        }
+        if (c == null) {
+            return null;
+        }
+
+        FOM_WebContinuation pwk = new FOM_WebContinuation(c);
+        pwk.setParentScope(getParentScope());
+        pwk.setPrototype(getClassPrototype(getParentScope(), pwk.getClassName()));
+        return pwk;
+    }
+
+    /**
+     * Return text representation of the WebContinuation.
+     */
+    public String toString() {
+        return "WC" + wk.getId();
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/FOM_WebContinuation.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocal.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocal.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocal.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocal.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.struts.flow.core.javascript.fom;
+
+import org.mozilla.javascript.Scriptable;
+
+/**
+ * @version CVS $Id: PageLocal.java 36239 2004-08-11 18:28:06Z vgritsenko $
+ */
+public interface PageLocal extends Scriptable {
+
+    public Object getId();
+
+    public void setPageLocalScope(PageLocalScope scope);
+
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocal.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalImpl.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalImpl.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalImpl.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalImpl.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,119 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.struts.flow.core.javascript.fom;
+
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * @version CVS $Id: PageLocalImpl.java 36239 2004-08-11 18:28:06Z vgritsenko $
+ */
+public class PageLocalImpl extends ScriptableObject implements PageLocal {
+
+    private PageLocalScope scope; // null if this is the prototype
+    private String id;
+
+    public PageLocalImpl() {
+        this.id = String.valueOf(System.identityHashCode(this));
+    }
+
+    public void setPageLocalScope(PageLocalScope scope) {
+        this.scope = scope;
+    }
+
+    public Object getId() {
+        return id;
+    }
+
+    public String getClassName() {
+        return "PageLocal";
+    }
+
+    public boolean has(String name, Scriptable start) {
+        if (scope == null) {
+            return super.has(name, start);
+        }
+        return scope.has(this, name);
+    }
+
+    public boolean has(int index, Scriptable start) {
+        if (scope == null) {
+            return super.has(index, start);
+        }
+        return scope.has(this, index);
+    }
+
+    public void put(String name, Scriptable start, Object value) {
+        if (scope == null) {
+             super.put(name, start, value);
+        } else {
+            scope.put(this, name, value);
+        }
+    }
+
+    public void put(int index, Scriptable start, Object value) {
+        if (scope == null) {
+             super.put(index, start, value);
+        } else {
+            scope.put(this, index, value);
+        }
+    }
+
+    public Object get(String name, Scriptable start) {
+        if (scope == null) {
+            return super.get(name, start);
+        }
+        return scope.get(this, name);
+    }
+
+    public Object get(int index, Scriptable start) {
+        if (scope == null) {
+            return super.get(index, start);
+        }
+        return scope.get(this, index);
+    }
+
+    public void delete(int index) {
+        if (scope == null) {
+            super.delete(index);
+        } else {
+            scope.delete(this, index);
+        }
+    }
+
+    public void delete(String name) {
+        if (scope == null) {
+            super.delete(name);
+        } else {
+            scope.delete(this, name);
+        }
+    }
+
+    public Object[] getIds() {
+        if (scope == null) {
+            return super.getIds();
+        }
+        return scope.getIds(this);
+    }
+
+    public Object getDefaultValue(Class hint) {
+        if (scope == null) {
+            return super.getDefaultValue(hint);
+        }
+        return scope.getDefaultValue(this, hint);
+    }
+
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalImpl.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalScope.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalScope.java?rev=349187&view=auto
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalScope.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalScope.java Sat Nov 26 23:10:08 2005
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.struts.flow.core.javascript.fom;
+
+/**
+ * @version CVS $Id: PageLocalScope.java 36239 2004-08-11 18:28:06Z vgritsenko $
+ */
+public interface PageLocalScope {
+
+    public boolean has(PageLocal local, String name);
+
+    public boolean has(PageLocal local, int index);
+
+    public Object get(PageLocal local, String name);
+
+    public Object get(PageLocal local, int index);
+
+    public void put(PageLocal local, String name, Object value);
+
+    public void put(PageLocal local, int index, Object value);
+
+    public void delete(PageLocal local, String name);
+
+    public void delete(PageLocal local, int index);
+
+    public Object[] getIds(PageLocal local);
+
+    public Object getDefaultValue(PageLocal local, Class hint);
+
+    public PageLocal createPageLocal();
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/core/javascript/fom/PageLocalScope.java
------------------------------------------------------------------------------
    svn:executable = *



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org